Windows socket,socket编程语言

  Windows socket,socket编程语言

  转载地址:3358 hi . Baidu . com/huangfei 564/blog/item/c 8 c 9 c 8 e 6b 323 fc 503d 9202 . html

  WinSock编程

  用WinSock API编程要了解TCP/IP的基础知识。虽然可以直接使用WinSock API编写网络应用,但是要编写优秀的网络应用,必须具备一定的TCP/IP协议知识。

  1.TCP/IP协议与WinSock网络编程接口的关系

  WinSock不是一个网络协议,它只是一个网络编程接口,也就是说,它不是一个协议,但是它可以访问很多种网络协议,你可以把它看成是一些协议的封装。现在WinSock已经基本实现了,和协议无关。可以用WinSock调用各种协议的函数。那么,WinSock和TCP/IP协议是什么关系呢?实际上,WinSock是TCP/IP协议的一种封装。通过调用WinSock的接口函数,可以调用TCP/IP的各种函数。比如我要用TCP/IP协议发送数据,你可以用WinSock的接口函数Send()调用TCP/IP发送数据的函数。至于如何发送数据,WinSock已经为你打包了这个功能。

  2.TCP/IP协议简介

  TCP/IP协议涵盖的范围很广。它是一个四层协议,包括各种硬件和软件要求的定义。TCP/IP协议的确切术语应该是TCP/UDP/IP协议。UDP(用户数据报协议)是一种保护消息边界的协议,不保证可靠数据的传输。TCP(传输控制协议)是一种流协议。它提供可靠、有序、双向和面向连接的传输。

  保护消息边界是指传输协议将数据作为独立的消息在互联网上传输,接收方只能接收独立的消息。也就是说有一个保护消息边界,接收方一次只能接收到发送方的一个数据包。

  面向流是指无保护的消息保护边界。如果发送方连续发送数据,接收方可能在一个接收动作中接收到两个或多个数据包。

  比如说我们连续发三个包,大小分别是2k,4k,8k。这三个数据包已经到达接收端的网络堆栈。如果使用UDP协议,无论我们使用多大的接收缓冲区来接收数据,我们都必须有三个接收动作来接收所有的数据包。在使用TCP协议时,我们只需将接收缓冲区的大小设置在14k以上,就可以一次接收所有数据包,并且只需要一个接收动作。

  这是因为UDP协议保护消息边界,因此每条消息都是独立的。另一方面,流将数据视为一串数据流,它不认为数据是一次一条消息。所以很多人在使用TCP协议进行通信时,并不知道TCP是基于流的传输。连续发送数据时,他们往往知道TCP会丢包。实际上,当他们使用的缓冲区足够大时,他们可能一次接收两个或更多的数据包,而很多人往往会忽略这一点,只解析和检查第一个数据包,而忽略其他接收到的数据包。

  3.3的简单过程。WinSock编程

  WinSock编程分为两部分:服务器端和客户端。TCP服务器的一般流程如下:

  对于任何基于WinSock的编程,必须首先初始化WinSock DLL库。

  int WSAStarup(WORD wversion requested,LPWSADATA lpWsAData).

  WVersionRequested是我们需要使用的WinSock版本。

  调用这个接口函数可以初始化WinSock。

  然后,您必须创建一个套接字。

  SOCKET Socket(int af,int type,int protocol);

  套接字是WinSock通信的核心。WinSock通信的所有数据传输都是通过套接字完成的。套接字包含两条信息,一条是IP地址,另一条是端口号。使用这两条信息,您可以确定网络中的任何通信节点。

  调用Socket()接口函数创建套接字时,必须用需要通信的地址连接套接字。这种联系可以通过绑定函数来实现。

  int bind(SOCKET s,const struct sockaddr FAR* name,int name len);

  struct sockaddr_in{

  短sin _ family

  u _ short sin _ prot

  结构in _ addr sin _ addr

  char sin _ sero[8];

  }

  它包含需要建立连接的本地地址,包括地址族、IP和端口信息。sin_family字段必须设置为AF_INET,这告诉WinSock使用了IP地址族。Sin_prot是用于通信的端口号。Sin_addr是用于通信的IP地址信息。

  这里,我们还必须提到“大端”和“小端”。因为不同的计算机处理数据的方式不同,所以Intel X86处理器用‘小头’的形式来表示多字节的个数,也就是把低字节放在前面,高字节放在后面,而互联网的标准正好相反。因此,主机字节必须转换成网络字节的顺序。WinSock API提供了几个函数。

  将主机字节转换成网络字节的功能;

  u _ long hton l(u _ long host long);

  u _ short htons(u _ short host short);

  将网络字节转换成主机字节的功能;

  u _ long ntohl(u _ long netlong);

  u _ short ntohs(u _ short net short);

  这样,在设置IP地址和端口port时,必须先将主机字节转换成网络字节,才能使用Bind()函数绑定套接字和地址。

  绑定完成后,服务器必须建立一个监听队列来接收来自客户端的连接请求。

  int listen(SOCKET s,int backlog);

  这个函数可以将套接字转为监听模式。

  如果客户端有连接请求,我们还必须使用

  int accept(SOCKET s,struct sockaddr FAR* addr,int FAR * addrlen);

  接受客户的要求。

  现在基本上已经完成了服务器的建立,建立客户端的过程就是初始化WinSock,然后创建Socket,然后使用。

  int connect(SOCKET s,const struct sockaddr FAR* name,int name len);

  连接到服务器。

  下面是创建服务器端和客户端的最简单的例子:

  服务器创建:

  WSADATA wsd

  插座滑动;

  套接字sclient

  UINT端口=800;

  int iAddrSize

  struct sockaddr_in local,client

  WSAStartup(0x11,wsd);

  sListen=Socket ( AF_INET,SOCK_STREAM,IP poto _ IP);

  local.sin _ family=AF _ INET

  local . sin _ addr=htonl(in addr _ ANY);

  local . sin _ port=htons(port);

  bind( sListen,(struct sockaddr*) local,sizeof(local));

  听(sListen,5);

  sClient=accept( sListen,(struct sockaddr*) client,iAddrSize);

  客户端创建:

  WSADATA wsd

  套接字sClient

  UINT端口=800;

  char szIp[]= 127 . 0 . 0 . 1 ;

  int iAddrSize

  struct sockaddr_in服务器;

  WSAStartup(0x11,wsd);

  sClient=Socket ( AF_INET,SOCK_STREAM,IP poto _ IP);

  server.sin _ family=AF _ INET

  server . sin _ addr=inet _ addr(szIp);

  server . sin _ port=htons(port);

  connect( sClient,(struct sockaddr*) server,sizeof(server));

  当服务器和客户端之间的连接建立后,客户端和服务器都可以使用它。

  int send( SOCKET s,const char FAR* buf,int len,int flags);

  int recv( SOCKET s,char FAR* buf,int len,int flags);

  函数来接收和发送数据,因为TCP连接是双向的。

  当你想关闭通信链路时,任何一方都可以呼叫。

  int shutdown(SOCKET s,int how);

  关闭套接字的指定函数,然后调用

  int close SOCKET(SOCKET s);

  关闭套接字句柄,这样一个通信过程就完成了。

  注意:上面的代码没有任何检查函数返回值。网络编程的话,一定要检查任何一个WinSock API函数的调用结果,因为很多时候函数调用不一定成功。上面描述的函数,如果返回值类型是int,如果函数调用失败,都会返回SOCKET_ERROR。

  4.4的模型。WinSock编程

  以上介绍的只是最简单的WinSock通信方式,但实际上在很多网络通信中有很多难以解决的突发情况。

  例如,WinSock提供了两种套接字模式:锁定和解锁。使用锁定插座时,会用到许多功能,如accpet、send、recv等。如果没有要处理的数据,就不会返回,也就是说,你的应用程序会阻塞那些函数的调用。而如果使用非阻塞模式,调用这些函数,不管有没有数据,它都会返回。所以有可能我们在非阻塞模式下调用这些函数的时候,大多数情况下都会返回失败,所以我们需要处理很多意想不到的错误。

  这显然不是我们希望看到的。我们可以使用WinSock通信模型来避免这些情况。

  WinSock提供了五种套接字I/O模型来解决这些问题。它们是select、WSAAsyncSelect、WSAEventSelect、overlapped和completion端口。

  下面是select和WSAASyncSelect的两个模型。

  选择模型是最常见的I/O模型。使用

  int select( int nfds,fd_set FAR* readfds,fd_set FAR* writefds,fd_set FAR* exceptfds,const struct time val FAR * time out);

  函数检查要调用的套接字是否已经有要处理的数据。

  Select包含三个套接字队列,分别代表:

  Readfds,检查可读性,writefds,检查可写性,exceptfds,异常数据。

  超时是选择函数的返回时间。

  例如,要检查套接字是否有数据要接收,我们可以将套接字句柄添加到可读性检查队列中,然后调用select。如果没有要接收的数据,select函数会将socket从可读性检查队列中删除,所以我们只要检查socket句柄是否还存在于可读性检查队列中,就可以知道是否有要接收的数据。

  WinSock提供了一些宏来操作套接字队列fd_set。

  FD_CLR( s,*set)从队列集中删除句柄。

  FD_ISSET( s,*set)检查队列集合中是否存在句柄s。

  FD_SET( s,*set)将句柄s添加到队列集合。

  FD_ZERO( *set)将集合队列初始化为空队列。

  WSAASyncSelect(异步选择)模型:WSAAsyncSelect模型是在窗口和套接字句柄之间建立连接。当socket的一个网络事件发生时,会向窗口发送一个消息,然后在窗口的消息响应函数中可以接收和发送数据。

  int WSAAsyncSelect( SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);

  这个函数可以将套接字句柄连接到窗口,

  WMsg是我们必须定制的消息。

  LEvent是一个既定的网络事件。包括FD _ read、FD _ write、FD _ accept、FD _ connect和FD _ close。几个事件。

  例如,需要接收FD _ READ、FD _ WRITE和FD _ CLOSE等网络事件。你可以打电话

  WSAAsyncSelect( s,hWnd,WM_SOCKET,FD _ READ FD _ WRITE FD _ CLOSE);

  这样,当有FD_READ、FD_WRITE或FD_CLOSE网络事件时,窗口hWnd会接收到WM_SOCKET消息,消息参数的lParam标记发生了什么事件。MFC的CSSocket类使用这个模型。

Windows socket,socket编程语言