您的位置:首页 > 理论基础 > 计算机网络

Socket网络编程学习笔记

2013-10-30 22:00 337 查看

Socket网络编程学习笔记

一、IP地址操作类

1、IPAddress类

a、在该类中有一个 Parse()方法,可以把点分的十进制IP表示转化成IPAddress类,方法如下:

IPAddress address = IPAddress.Parse(“192.168.0.1”);

b、IPAddress提供4个只读字段



Any 用于代表本地系统可用的任何IP地址

Broadcase 用于代表本地网络的IP广播地址

Loopback 用于代表系统的回送地址

None 用于代表系统上没有网络接口

其中IPAddress.Any最常用可以用来表示本机上所有的IP地址,这对于socket服务进行侦听时,最方便使用,不用对每个IP进行侦听了。而IPAddress.Broadcase可用来UDP的IP广播,这些具体讲socket时再详细介绍。

2、IPEndPoint类

我们可以通过二种构造方法来创建IPEndPoint类:

a、IPEndPoint(long address, int port)

b、IPEndPoint(IPAddress address, int port)

四个属性:



Address

AddressFamily

Port

MaxPort

MinPort

这些应该从名字上就很好理解,不再一一介绍。IPEndPoint其实就是一个IP地址和端口的绑定,可以代表一个服务,用来Socket通讯。

二、DNS相关类

DNS类有四个静态方法,来获取主机DNS相关信息,

1、GetHostName()

通过Dns.GetHostName()可以获得本地计算机的主机名



2、GetHostByName()

根据主机名称,返回一个IPHostEntry 对象:



IPHostEntry GetHostByName(string hostName)

其中IPHostEntry把一个DNS主机名与一个别名和IP地址的数组相关联,包含三个属性:

AddressList:一个IPAddress对象的数组

Aliases:一个字符串对象数组

HostName:一个用于主机名的字符串对象

3、GetHostByAddress()

类似于GetHostByName(),只不过这里的参数是IP地址,而不是主机名,也返回一个IPHostEntry对象。

IPHostEntry GetHostByAddress(IPAddress address)

IPHostEntry GetHostByAddress(string address)

4、Resolve()

当我们不知道输入的远程主机的地址是哪种格式时(主机名或IP地址),用以上的二种方法来实现,我们可能还要通过判断客户输入的格式,才能正确使用,但Dns类提供一更简单的方法Resolve(),该方法可以接受或者是主机名格式或者是IP地址格式的任何一种地址,并返回IPHostEntry对象。

一、服务端Socket侦听:

服务端Socket侦听主要分以下几个步骤,按照以下几个步骤我们可以很方便的建立起一个Socket侦听服务,来侦听尝试连接到该服务器的客户Socket,从而建立起连接进行相关通讯。

1、创建IPEndPoint实例,用于Socket侦听时绑定

IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 6001);

2、创建套接字实例

serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

这里创建的时候用ProtocolType.Tcp,表示建立一个面向连接(TCP)的Socket。

3、将所创建的套接字与IPEndPoint绑定

serverSocket.Bind(ipep);

4、设置套接字为收听模式

serverSocket.Listen(10);

以上这四步,我们已经建立了Socket的侦听模式,下面我们就来设置怎么样来获取客户Socket连接的实例,以及连接后的信息发送。

5、在套接字上接收接入的连接

while (true){

try{

//在套接字上接收接入的连接

clientSocket = serverSocket.Accept();

clientThread = new Thread(new ThreadStart(ReceiveData));

clientThread.Start();

}catch (Exception ex){

MessageBox.Show("listening Error: " + ex.Message);

}

}

通过serverSocket.Accept()来接收客户Socket的连接请求,在这里用循环可以实现该线程实时侦听,而不是只侦听一次。当程序运行serverSocket.Accept()时,会等待,直到有客户端Socket发起连接请求时,获取该客户Socket,如上面的clientSocket。在这里我用多线程来实现与多个客户端Socket的连接和通信,一旦接收到一个连接后,就新建一个线程,执行ReceiveData功能来实现信息的发送和接收。

6、 在套接字上接收客户端发送的信息和发送信息

private void ReceiveData(){

bool keepalive = true;

Socket s = clientSocket;

Byte[] buffer = new Byte[1024];

//根据收听到的客户端套接字向客户端发送信息

IPEndPoint clientep = (IPEndPoint)s.RemoteEndPoint;

lstServer.Items.Add

"Client:" + clientep.Address + "("+clientep.Port+")");

string welcome = "Welcome to my test sever ";

byte[] data = new byte[1024];

data = Encoding.ASCII.GetBytes(welcome);

s.Send(data, data.Length, SocketFlags.None);

while (keepalive){

//在套接字上接收客户端发送的信息

int bufLen = 0;

try{

bufLen = s.Available;

s.Receive(buffer, 0, bufLen, SocketFlags.None);

if (bufLen == 0){

continue;

}catch (Exception ex){

MessageBox.Show("Receive Error:" + ex.Message);

return;

}clientep = (IPEndPoint)s.RemoteEndPoint;

string clientcommand = System.Text.Encoding.ASCII.GetString (buffer).Substring(0, bufLen);

lstServer.Items.Add (clientcommand + "("+clientep.Address + ":"+clientep.Port+")");

}

}

通过IPEndPoint clientep = (IPEndPoint)s.RemoteEndPoint;我们可以获取连接上的远程主机的端口和IP地址,如果想查询该主机的其它属性如主机名等,可用于上一篇讲的Dns.GetHostByAddress(string ipAddress)来返回一个IPHostEntry对象,就可以得到。另外我们要注意的是,通过Socket发送信息,必须要先把发送的信息转化成二进字进行传输,收到信息后也要把收到的二进字信息转化成字符形式,这里可以通过Encoding.ASCII.GetBytes(welcome);和Encoding.ASCII.GetString(buffer).Substring(0, bufLen);来实现。

以上就是服务端Socket侦听模式的实现,只要有远程客户端Socket连接上后,就可以轻松的发送信息和接收信息了。下面我们来看看客户端Socket是怎么连接上服务器的。

二、客户端连接

客户端Socket连接相对来说比较简单了,另外说明一下,在执行客户端连接前,服务端Socket侦听必须先启动,不然会提示服务器拒绝连接的信息。

1、创建IPEndPoint实例和套接字

IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6001);

clientSocket = new SocketAddressFamily.InterNetwork, SocketType.Stream,

ProtocolType.Tcp);

这个跟服务端Socket侦听差不多,下面一步由服务端Socket的侦听模式变成连接模式。

2、将套接字连接到远程服务器

//将套接字与远程服务器地址相连

try{

clientSocket.Connect(ipep);

}

catch (SocketException ex){

MessageBox.Show("connect error: " + ex.Message);

return;

}

前面已说明,如果在执行Socket连接时,服务器的Socket侦听没有开启的话,会产生一个SocketException异常,如果没有异常发生,那恭喜你,你已经与服务器连接上了,接下来就可以跟服务器通信了。

3、接收信息

while (true){

//接收服务器信息

int bufLen = 0;

try

{

bufLen = clientSocket.Available;

clientSocket.Receive(data, 0, bufLen, SocketFlags.None);

if (bufLen == 0)

continue;

}

}

catch (Exception ex)

{

MessageBox.Show("Receive Error:" + ex.Message);

return;

}

string clientcommand = System.Text.Encoding.ASCII.GetString(data).Substring(0, bufLen);

lstClient.Items.Add(clientcommand);

}

4、发送信息

//向服务器发送信息

byte[] data = new byte[1024];

data = Encoding.ASCII.GetBytes(txtClient.Text);

clientSocket.Send(data, data.Length, SocketFlags.None);

客户端的发送信息和接收信息跟服务器的接收发送是一样的,只不过一个是侦听模式而另一个是连接模式。





打开Socket

命 名

监听端口

建立连接

收发消息

关闭连接

打开Socket



连接服务器

收发消息

关闭连接

服务器端程序

客户端程序

基于TCP的socket编程

关键词: VC

sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);基于TCP的socket编程是采用的流式套接字。

在这个程序中,将两个工程添加到一个工作区。要链接一个ws2_32.lib的库文件。

服务器端编程的步骤:

1:加载套接字库,创建套接字(WSAStartup()/socket());

注明:SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

socket()构照函数初始化SOCKET 类的实例

2:绑定套接字到一个IP地址和一个端口上(bind());

注明:使 Socket 与一个本地终结点相关联。

3:将套接字设置为监听模式等待连接请求(listen());

注明:The listen function places a socket in a state

int listen(

SOCKET s,

int backlog(最大链接数)

);



4:请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());

注明:为新建连接创建新的 Socket。

5:用返回的套接字和客户端进行通信(send()/recv());

6:返回,等待另一连接请求;

7:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

服务器端代码如下:

#include <stdio.h>

#include <Winsock2.h>

void main()

{

WORD wVersionRequested;

WSADATA wsaData;

int err;



wVersionRequested = MAKEWORD( 1, 1 );



err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}



if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}

SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_IN addrSrv;

addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

addrSrv.sin_family=AF_INET;

addrSrv.sin_port=htons(6000);



bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

listen(sockSrv,5);

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);

while(1)

{

SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

char sendBuf[50];

sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));

send(sockConn,sendBuf,strlen(sendBuf)+1,0);

char recvBuf[50];

recv(sockConn,recvBuf,50,0);

printf("%s\n",recvBuf);

closesocket(sockConn);

}

}

客户端编程的步骤:

1:加载套接字库,创建套接字(WSAStartup()/socket());

2:向服务器发出连接请求(connect());

注明:建立与远程主机的连接。

3:和服务器端进行通信(send()/recv());

4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

客户端的代码如下:

#include <stdio.h>

#include <Winsock2.h>

void main()

{

WORD wVersionRequested;

WSADATA wsaData;

int err;



wVersionRequested = MAKEWORD( 1, 1 );



err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}



if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}

SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);



SOCKADDR_IN addrSrv;

addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

addrSrv.sin_family=AF_INET;

addrSrv.sin_port=htons(6000);

connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

char recvBuf[50];

recv(sockClient,recvBuf,50,0);

printf("%s\n",recvBuf);

send(sockClient,"hello",strlen("hello")+1,0);



closesocket(sockClient);

WSACleanup();

}





基于Socket的UDP和TCP编程介绍

  作者:王姗姗,华清远见嵌入式学院讲师。

广告插播信息

维库最新热卖芯片: ADM705AR IDT71024S15Y AM27C256-150DC AD7225LR PC900 AD239JR CY7C144-35AC MC10H159FN CD54HC74F3A MC100EL34DR2

  一、概述

  TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议。

  TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流,TCP套接口是字节流套接口(stream socket)的一种。

  UDP:用户数据报协议。UDP是一种无连接协议。UDP套接口是数据报套接口(datagram socket)的一种。

  二、TCP和UDP介绍

  1)基本TCP客户—服务器程序设计基本框架



  说明:(三路握手)

  1.客户端发送一个SYN段(同步序号)指明客户打算连接的服务器端口,以及初始化序号(ISN) 。

  2.服务器发回包含服务器的初始序号的SYN报文段作为应答。同时,将确认序号(ACK)设置为客户的ISN加1以对客户的SYN 报文段进行确认。一个SYN将占用一个序号。

  3.客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认。

  2) 基本TCP客户—服务器程序设计基本框架流程图



  3) UDP和TCP的对比:

  从上面的流程图比较我们可以很明显的看出UDP没有三次握手过程。

  简单点说。UDP处理的细节比TCP少。UDP不能保证消息被传送到(它也报告消息没有传送到)目的地。UDP也不保证数据包的传送顺序。UDP把数据发出去后只能希望它能够抵达目的地。

  TCP优缺点:

  优点:

  1.TCP提供以认可的方式显式地创建和终止连接。

  2.TCP保证可靠的、顺序的(数据包以发送的顺序接收)以及不会重复的数据传输。

  3.TCP处理流控制。

  4.允许数据优先

  5.如果数据没有传送到,则TCP套接口返回一个出错状态条件。

  6.TCP通过保持连续并将数据块分成更小的分片来处理大数据块。—无需程序员知道

  缺点: TCP在转移数据时必须创建(并保持)一个连接。这个连接给通信进程增加了开销,让它比UDP速度要慢。

  UDP优缺点:

  1.UDP不要求保持一个连接

  2.UDP没有因接收方认可收到数据包(或者当数据包没有正确抵达而自动重传)而带来的开销。

  3.设计UDP的目的是用于短应用和控制消息

  4.在一个数据包连接一个数据包的基础上,UDP要求的网络带宽比TDP更小。

  三、Socket编程

  Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。

  Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

  1、socket调用库函数主要有:

  创建套接字

  Socket(af,type,protocol)

  建立地址和套接字的联系

  bind(sockid, local addr, addrlen)

  服务器端侦听客户端的请求

  listen( Sockid ,quenlen)

  建立服务器/客户端的连接 (面向连接TCP)

  客户端请求连接

  Connect(sockid, destaddr, addrlen)

  服务器端等待从编号为Sockid的Socket上接收客户连接请求

  newsockid=accept(Sockid,Clientaddr, paddrlen)

  发送/接收数据

  面向连接:send(sockid, buff, bufflen)

  recv( )

  面向无连接:sendto(sockid,buff,…,addrlen)

  recvfrom( )

  释放套接字

  close(sockid)

  2、TCP/IP应用编程接口(API)

  服务器的工作流程:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用listen在相应的socket上*,当accpet接收到一个连接服务请求时,将生成一个新的socket。服务器显示该客户机的IP地址,并通过新的socket向客户端发送字符串" hi,I am server!"。最后关闭该socket。

main()

{

int sock_fd,client_fd; /*sock_fd:*socket;client_fd:数据传输socket */

struct sockaddr_in ser_addr; /* 本机地址信息 */

struct sockaddr_in cli_addr; /* 客户端地址信息 */

char msg[MAX_MSG_SIZE];/* 缓冲区*/

ser_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */

if(ser_sockfd<0)

{/*创建失败 */

fprintf(stderr,"socker Error:%s\n",strerror(errno));

exit(1);

}

/* 初始化服务器地址*/

addrlen=sizeof(struct sockaddr_in);

bzero(&ser_addr,addrlen);

ser_addr.sin_family=AF_INET;

ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);

ser_addr.sin_port=htons(SERVER_PORT);

if(bind(ser_sockfd,(struct sockaddr*)&ser_addr,sizeof(struct sockaddr_in))<0)

{ /*绑定失败 */

fprintf(stderr,"Bind Error:%s\n",strerror(errno));

exit(1);

}

/*侦听客户端请求*/

if(listen(ser_sockfd,BACKLOG)<0)

{

fprintf(stderr,"Listen Error:%s\n",strerror(errno));

close(ser_sockfd);

exit(1);

}

while(1)

{/* 等待接收客户连接请求*/

cli_sockfd=accept(ser_sockfd,(struct sockaddr*) & cli_addr,&addrlen);

if(cli_sockfd<=0)

{

fprintf(stderr,"Accept Error:%s\n",strerror(errno));

}

else

{/*开始服务*/

recv(cli_addr,msg,MAX_MSG_SIZE,0); /* 接受数据*/

printf("received a connection from %sn", inet_ntoa(cli_addr.sin_addr));

printf("%s\n",msg);/*在屏幕上打印出来 */

strcpy(msg,"hi,I am server!");

send(cli_addr,msg,sizeof(msg),0); /*发送的数据*/

close(cli_addr);

}

}

close(ser_sockfd);

}

  客户端的工作流程:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,请求连接服务器,通过新的socket向客户端发送字符串" hi,I am client!"。最后关闭该socket。

main()

{

int cli_sockfd;/*客户端SOCKET */

int addrlen;

char seraddr[14];

struct sockaddr_in ser_addr,/* 服务器的地址*/

cli_addr;/* 客户端的地址*/

char msg[MAX_MSG_SIZE];/* 缓冲区*/

GetServerAddr(seraddr);

cli_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */

if(ser_sockfd<0)

{/*创建失败 */

fprintf(stderr,"socker Error:%s\n",strerror(errno));

exit(1);

}

/* 初始化客户端地址*/

addrlen=sizeof(struct sockaddr_in);

bzero(&ser_addr,addrlen);

cli_addr.sin_family=AF_INET;

cli_addr.sin_addr.s_addr=htonl(INADDR_ANY);

cli_addr.sin_port=0;

if(bind(cli_sockfd,(struct sockaddr*)&cli_addr,addrlen)<0)

{

/*棒定失败 */

fprintf(stderr,"Bind Error:%s\n",strerror(errno));

exit(1);

}

/* 初始化服务器地址*/

addrlen=sizeof(struct sockaddr_in);

bzero(&ser_addr,addrlen);

ser_addr.sin_family=AF_INET;

ser_addr.sin_addr.s_addr=inet_addr(seraddr);

ser_addr.sin_port=htons(SERVER_PORT);

if(connect(cli_sockfd,(struct sockaddr*)&ser_addr,&addrlen)!=0)/*请求连接*/

{

/*连接失败 */

fprintf(stderr,"Connect Error:%s\n",strerror(errno));

close(cli_sockfd);

exit(1);

}

strcpy(msg,"hi,I am client!");

send(sockfd,msg,sizeof(msg),0);/*发送数据*/

recv(sockfd,msg,MAX_MSG_SIZE,0); /* 接受数据*/

printf("%s\n",msg);/*在屏幕上打印出来 */

close(cli_sockfd);

}

  3、UDP/IP应用编程接口(API)

  服务器的工作流程:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,接收到一个客户端时,服务器显示该客户端的IP地址,并将字串返回给客户端。

int main(int argc,char **argv)

{

int ser_sockfd;

int len;

//int addrlen;

socklen_t addrlen;

char seraddr[100];

struct sockaddr_in ser_addr;

/*建立socket*/

ser_sockfd=socket(AF_INET,SOCK_DGRAM,0);

if(ser_sockfd<0)

{

printf("I cannot socket success\n");

return 1;

}

/*填写sockaddr_in 结构*/

addrlen=sizeof(struct sockaddr_in);

bzero(&ser_addr,addrlen);

ser_addr.sin_family=AF_INET;

ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);

ser_addr.sin_port=htons(SERVER_PORT);

/*绑定客户端

if(bind(ser_sockfd,(struct sockaddr *)&ser_addr,addrlen)<0)

{

printf("connect");

return 1;

}

while(1)

{

bzero(seraddr,sizeof(seraddr));

len=recvfrom(ser_sockfd,seraddr,sizeof(seraddr),0,(struct sockaddr*)&ser_addr,&addrlen);

/*显示client端的网络地址*/

printf("receive from %s\n",inet_ntoa(ser_addr.sin_addr));

/*显示客户端发来的字串*/

printf("recevce:%s",seraddr);

/*将字串返回给client端*/

sendto(ser_sockfd,seraddr,len,0,(struct sockaddr*)&ser_addr,addrlen);

}

}

  客户端的工作流程:首先调用socket函数创建一个Socket,填写服务器地址及端口号,从标准输入设备中取得字符串,将字符串传送给服务器端,并接收服务器端返回的字符串。最后关闭该socket。

int GetServerAddr(char * addrname)

{

printf("please input server addr:");

scanf("%s",addrname);

return 1;

}

int main(int argc,char **argv)

{

int cli_sockfd;

int len;

socklen_t addrlen;

char seraddr[14];

struct sockaddr_in cli_addr;

char buffer[256];

GetServerAddr(seraddr);

/* 建立socket*/

cli_sockfd=socket(AF_INET,SOCK_DGRAM,0);

if(cli_sockfd<0)

{

printf("I cannot socket success\n");

return 1;

}

/* 填写sockaddr_in*/

addrlen=sizeof(struct sockaddr_in);

bzero(&cli_addr,addrlen);

cli_addr.sin_family=AF_INET;

cli_addr.sin_addr.s_addr=inet_addr(seraddr);

//cli_addr.sin_addr.s_addr=htonl(INADDR_ANY);

cli_addr.sin_port=htons(SERVER_PORT);

bzero(buffer,sizeof(buffer));

/* 从标准输入设备取得字符串*/

len=read(STDIN_FILENO,buffer,sizeof(buffer));

/* 将字符串传送给server端*/

sendto(cli_sockfd,buffer,len,0,(struct sockaddr*)&cli_addr,addrlen);

/* 接收server端返回的字符串*/

len=recvfrom(cli_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&cli_addr,&addrlen);

//printf("receive from %s\n",inet_ntoa(cli_addr.sin_addr));

printf("receive: %s",buffer);

close(cli_sockfd);

}

  四、调试

  Makefile文件为:

CC=gcc

all:server client

CFLAGS=-o

server: server.c

$(CC) $(CFLAGS) $@ server.c

client: client.c

$(CC) $(CFLAGS) $@ client.c

clean:

rm -f server client

  在shell中执行make进行编译,make clean删除生成文件。

  运行结果如下图:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: