面向连接的通信和无连接的通信

来源:互联网 发布:医疗器械软件注册流程 编辑:程序博客网 时间:2024/05/21 00:00

1.面向连接的通信

在IP中,面向连接的通信时通过TCP/IP协议来实现的。应用程序在使用TCP通信时,需要建立一个虚拟连接。其模型如下:

sd

服务器端

一旦为协议创建了套接字,就要将套接字绑定到一个已知地址上,用bind函数来实现。其定义如下:

[cpp] view plaincopy
  1. int bind(  
  2.    SOCKET      s,  
  3.    const struct sockaddr FAR *name,  
  4.    int namelen  
  5. );  
  • s:为要连接的套接字。
  • name:其为类型为struct sockaddr。对于TCP协议要用结构体SOCKADDR_IN,要将该结构体转换为该类型。
  • namelen:表示要传递的、由协议决定的地址结构的长度,即第二个参数的长度。

 当将套接字绑定后,就是将套接字置于监听状态,函数为listen。其定义如下:

[cpp] view plaincopy
  1. int listen(  
  2.    SOCKET s,  
  3.    int    backlog  
  4. );  
  • s:其为绑定的套接字。
  • backlog:表示等待连接队列的最大长度。当服务器接受了一个连接,就将该连接请求从该队列中删除;当连接请求超过队列长度,就会发回WSAECONNREFUSED错误。

为了接受连接请求,要使用函数accept、WSAAccept或AcceptEx来实现。其中accept函数定义如下:

[cpp] view plaincopy
  1. SOCKET accept(  
  2.      SOCKET   s,  
  3.      struct sockaddr FAR* addr,  
  4.      int FAR* addrlen  
  5. );  
  • s:为绑定的套接字。
  • addr:为TCP应该是一个有效的SOCKADDR_IN的地址,如果是其他协议就应该是相应的SOCKADDR结构。
  • addr:为SOCKADDR_IN结构体的长度。
  • 返回值:其返回一个新的套接字描述符,其后与客服端的所有操作都应该使用该新的套接字。

通过accept函数,可以讲等待连接队列的第一个请求提供服务。accept返回后,addr会被相应的对方的IP4信息填充。
客户端

客户端创建需要下面3个步骤:

  • 1)创建一个套接字。
  • 2)建立一个SOCKADDR地址结构。其为服务器的IP地址和端口号。
  • 3)用connect或WSAConnect函数来与服务器建立连接。

连接套接字通过函数conenct、WSAConnect或ConnectEx来完成。其中connect函数的定义如下:

[cpp] view plaincopy
  1. int connect(  
  2.     SOCKET s,  
  3.     const struct sockaddr FAR* name,  
  4.     int namelen  
  5. );  
  • s:为创建的套接字。
  • name:是TCP的套接字地址结构SOCKADDR_IN,其为要连接的服务器。
  • namelen:为name的长度。

数据传输

要在已建立的套接字上发送数据,可以使用两个函数:send和WSASend。其中send函数的定义如下:

[cpp] view plaincopy
  1. int send(  
  2.     SOCKET s,  
  3.     const char FAR* buf,  
  4.     int len,  
  5.     int flags  
  6. );  
  • s:为已建立的、用于发送数据的套接字。
  • buf:其为指向字符的缓冲区,其为要发送的数据。
  • len:指向发送的缓冲区的字符数,即要发送的数据长度。
  • flags:可为0、MSG_DONTROUTE、MSG_OOB(紧急数据)。支持“或”运算。
  • 返回值:执行成功返回发送的字节数,执行错误返回SOCKET_ERROR。

Send API的Winsock 2版本的函数为WSASend,其定义如下:

[cpp] view plaincopy
  1. int WSASend(  
  2.     SOCKET s.  
  3.     LPWSABUF lpBuffers,  
  4.     DWORD dwBufferCount,  
  5.     LPDWORD lpNumberOfByteSent,  
  6.     DWORD dwFlags,  
  7.     LPWSAOVERLAPPED lpOverlapped,  
  8.     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  
  9. );  
  •  s:为准备发送数据的套接字。
  • lpBuffers:指向一个或多个WSABUF结构的指针。既可以是一个独立的结构,也可以是一个结构数组。
  • ldwBufferCount:传递的WSABUF结构的数量。
  • lpNumberOfByteSent:其为已经发送了的字节数。
  • dwFlags:其可为为MSG_PEEK、MSG_OOB和MSG_PARTIAL,支持“或”运算。
  • lpOverlapped和lpCompletionRoutine:用于重叠I/O。
  • 返回值:执行成功返回0;失败返回SOCK_ERROR。

        一个特殊的传输函数,该函数起初将套接字置于关闭状态,并发送断开的数据。只适用于从容关机和断开数据的传输协议。该函数的行为与利用SD_SEND参数调用shutdown函数差不多,但它还要发送参数boundDisconnectData中的数据。之后发送禁止在该套接字上进行。发送失败发挥SOCKET_ERROR。该函数为WSASendDisconnect。其定义如下:

[cpp] view plaincopy
  1. int WSASendDisconnect(  
  2.    SOCKET s,  
  3.    LPWSABUF  lpOutboundDisconnectData  
  4. );  

 

在已经连接的套接字上接收数据,有三个函数recv、WSARecv和WSARecvDisconnect。其中recv函数的定义如下:

[cpp] view plaincopy
  1. int recv(  
  2.    SOCKET s,  
  3.    char FAR *buf,  
  4.    int len,  
  5.    int flags  
  6. );  
  • s:为准备接收数据的套接字。
  • buf:用于接收数据的缓冲区。
  • len:准备接收的字节数或buf缓冲区的长度。
  • flags:与send函数相同:0、MSG_PEEK、MSG_OOB。其中0表示什么行为;MSG_PEEK表示将可用数据复制到用户缓冲区中,但不从系统的缓冲区中删除。
  • 返回值:返回获取的数据字节数。

WSARecv函数在recv函数的基础上增加了一些新特性,如重叠I/O和部分数据报通知。其定义如下:

[html] view plaincopy
  1. int WSARecv(  
  2.     SOCKET s.  
  3.     LPWSABUF lpBuffers,  
  4.     DWORD dwBufferCount,  
  5.     LPDWORD lpNumberOfByteRecvd,  
  6.     DWORD dwFlags,  
  7.     LPWSAOVERLAPPED lpOverlapped,  
  8.     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  
  9. );  
  • s:为准备接收数据的套接字。
  • lpBuffers:指向一个或多个WSABUF结构的指针。既可以是一个独立的结构,也可以是一个结构数组。
  • ldwBufferCount:传递的WSABUF结构的数量。
  • lpNumberOfByteRecvd:其为已经接收到的字节数。
  • dwFlags:其可为为MSG_PEEK、MSG_OOB和MSG_PARTIAL,支持“或”运算。
  • lpOverlapped和lpCompletionRoutine:用于重叠I/O。
  • 返回值:执行成功返回0;失败返回SOCK_ERROR。

WSARecvDisconnect函数的定义如下:

[cpp] view plaincopy
  1. int WSARecvDisconnect(  
  2.    SOCKET s,  
  3.    LPWSABUF  lpInboundDisconnectData  
  4. );  
  • 其中s为已建立连接的套接字,lpInboundDisconnectData为WSABUF结构的接收数据的缓冲区。其可以接收断开数据,只能接收由WSASendDisconnect函数发送的数据,不能接收普通数据。其接收完断开数据,就会取消接收远程通信方的数据,其作用和带参数的SD_RECEIVE的shutdown函数相同。

注意:在流协议上使用发送和和接收数据,无法保证接收和发送的数据量。

中断连接
      一旦用完了套接字连接,需要释放套接字连接,释放所有资源。可以有两个函数来实现:closesocket和shutdown。但是closesocket函数可能导致数据丢失,而shutdown就可以从容终止。

     为了保证通信方能够接收到应用程序发出的所有数据,好的应用程序,应该通知对方不要在发送数据。这个可以通过shutdown函数来实现,该函数定义如下:

[cpp] view plaincopy
  1. int shutdown(  
  2.    SOCKET s,  
  3.    int      how  
  4. );  
  • s:为要关闭的套接字。
  • how:为SD_SEND、SD_RECEIVE和SD_BOTH中一个。SD_RECEIVE表示不允许在调用接收函数,表示要重置连接。SD_SEND表示不允许在调用发送函数,对于TCP会将所有数据发送完且得到确认后,发送FIN包。SD_BOTH表示取消连接两端的收发操作。
  • 但并非所有的协议都支持从容关闭,因此需要注意。

closesocket函数用于关闭套接字。该函数会释放套接字描述符,并且会将所有队列中的数据。其定义如下:

[cpp] view plaincopy
  1. int closesocket(SOCKET s);   

 2.无连接通信

         对于无连接的协议,操作过程相对比较简单。首先使用socket和WSASocket函数初始化套接字,然后使用bind函数将套接字绑定到准备接收数据的接口上,然后使用recvfrom和WSARecvFrom函数来接收数据,使用sendto和WSASendTo函数来发送数据。由于是无连接的,就没从容关闭和正常关闭的说法,如果套接字使用完了,可以调用closesocket函数来释放套接字分配的相关资源。

 recvfrom函数的定义如下:

[cpp] view plaincopy
  1. int recvfrom(  
  2.   SOCKET s,  
  3.   char FAR *buf,  
  4.   int len,  
  5.   int flags,  
  6.   struct sockaddr FAR *from,  
  7.   int fromlen  
  8. );  
  • s、buf和len:和recv参数相同。
  • flags:其可为MSG_OOB、MS_PEEK。
  • from:其为一个SOCKADDR结构。
  • fromlen:为from参数的长度。

recvfrom在Winsock2中的版本为WSARecvFrom,其定义如下:

[cpp] view plaincopy
  1. int WSARecvFrom(     
  2.     SOCKET s.  
  3.     LPWSABUF lpBuffers,  
  4.     DWORD dwBufferCount,  
  5.     LPDWORD lpNumberOfByteRecvd,  
  6.     DWORD dwFlags,  
  7.     struct sockaddr FAR* lpfrom,  
  8.     LPINT lpFromlen,  
  9.     LPWSAOVERLAPPED lpOverlapped,  
  10.     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  
  11. );  
  12. 发送函数sendto,其定义如下:  
[cpp] view plaincopy
  1. <pre class="cpp" name="code">int sendto(  
  2.   SOCKET s,  
  3.   char FAR *buf,  
  4.   int len,  
  5.   int flags,  
  6.   struct sockaddr FAR *to,  
  7.   int tolen</pre><pre class="cpp" name="code">);  
  8. </pre>  

sendto在Winsock2中的版本为WSASendTo,其定义如下:

[cpp] view plaincopy
  1. int WSASendTo(     
  2.     SOCKET s.  
  3.     LPWSABUF lpBuffers,  
  4.     DWORD dwBufferCount,  
  5.     LPDWORD lpNumberOfByteSent,  
  6.     DWORD dwFlags,  
  7.     struct sockaddr FAR* lpto,  
  8.     LPINT lptolen,  
  9.     LPWSAOVERLAPPED lpOverlapped,  
  10.     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  
  11. );  
  12. <strong>在无连接套接字上建立连接:无连接套接字一旦建立,就可以使用SOCKADDR结构体调用conenct和WSAConnect函数来建立“单向连接”,这样就可以使用recv和WSARecv函数或send和WSASend函数来进行收发数据了。</strong>  

3.一些重要的API函数

 1.getpeername

该函数用于获取通信方套接字的地址信息,该信息是关于已连接的那个套接字。其定义如下:

[cpp] view plaincopy
  1. int getpeername(  
  2.    SOCKET s,  
  3.    sruct sockaddr FAR* name,  
  4.    int FAR* namelen  
  5. );  

2.getsockname

该函数获取给定套接字的本地接口的地址信息。其定义如下:

[cpp] view plaincopy
  1. int getpsockname(  
  2.    SOCKET s,  
  3.    sruct sockaddr FAR* name,  
  4.    int FAR* namelen  
  5. );  

3.WSADuplicateSocket

WSADuplicateSocket函数用于建立WSAPROTOCOL_INFO结构,该结构体可传递到另一个进程。这样另一个进程就可以打开指向同一个套接字的句柄,这样这一个进程可以对该资源进行操作。其定义如下:

[cpp] view plaincopy
  1. int WSADuplicateSocket(  
  2.    SOCKET s,  
  3.    DWORD dwProcessId,  
  4.   LPWSAPROTOCOL_INFO lpProtocolInfo  
  5. );
0 0
原创粉丝点击