• 理解tcp顺序释放操作和tcp的半关闭


    Shutdown的调用
           在关闭socket的时候,可以有两种方式closesocket和shutdown,这两个函数的区别在什么地方呢?
    #include <sys/socket.h>             /*UNIX*/
    #include<winsock.h>               /*Windows*/
             int shutdown(int s, int how)           /*UNIX*/
    int shutdown(SOCKET s, int how)    /*Windows*/
     
    how
    操作
    数字
    POSIX
    Winsock
    0
    SHUT_RD
    SD_RECEIVE
    关闭连接接收方
    1
    SHUT_WR
    SD_SEND
    关闭连接的发送方
    2
    SHUT_RDWR
    SD_BOTH
    关闭双方
                                             Shutdown参数how值介绍
    说明:
    1.       shutdown根本没有关闭socket,任何与socket关联的资源直到调用closesocket才释放。
    2.       TCP连接的socket是全双工的,也就是说它可以发送和接收数据,但是一个方向上的数据流动和另一个方向上的数据流动是不相关的,shutdown函数的功能也就是体现在这里,它通过设置how选择关闭哪条数据通道(发送通道和接收通道),如果关闭了发送通道,那么这个socket其实还可以通过接收通道接受数据.
    3.       当通过以how=1的方式调用shutdown,就可以保证对方收到一个EOF,而不管其他的进程是否已经打开了套接字,而调用close或closesocket就不能保证,因为直到套接字的引用计数减为0时才会发送FIN消息给对方,也就是说,直到所有的进程都关闭了套接字。
    例子说明
     
     客户端(192.168.1.122): 客户端连接上服务器后向服务器发送两条数据(数据发送间隔为2秒),等待两秒后立即调用shutdown关闭发送数据通道或者closesocket,然后客户端在一个while循环中等待接受服务器发送回来的数据.
     服务器(192.168.1.112): 接受到客户端发送的数据后,休眠4秒后,把得到的数据发送会客户端
     
    1.       shutdown关闭发送数据通道后,通过抓包程序获取的tcp处理过程。
       
                    Shutdown关闭socket时,tcp的状态处理过程.
    对上面的处理过程进行分析:
    l         前面的三条记录是tcp的三次握手.
    l         第四条记录就是客户端第一次向服务器发送数据
    l         第五条记录是服务器的确认信息
    l         第六条记录就是客户端间隔2秒后第二次向服务器发送数据
    l         第七条记录时服务器的确认信息
    l         第八条记录是在等待2秒后,调用了shutdown,客户端向服务器发送了FIN标志
    l         第九条记录时服务器的确认信息
    l         第十条记录是服务器接收到客户端的一条记录后休眠4秒,发回给客户端
    l         第十一条记录是客户端的确认
    l         第十二条记录是服务器接受到客户端的第二条数据后休眠4秒,发回给客户端
    l         第十三条记录是客户端的确认
    l         第十四条记录是服务器发送完tcp对列中的数据后,向客户端发送FIN标志
    l         第十五条记录时客户端对FIN标志的确认
     
    2.       closesocket关闭socket,通过抓包程序获取的tcp处理过程
    Closesocket关闭socket时,tcp的状态处理过程
     
     
    对上面的处理过程进行分析:
    l         前面的三条记录是tcp的三次握手.
    l         第四条记录就是客户端第一次向服务器发送数据
    l         第五条记录是服务器的确认信息
    l         第六条记录就是客户端间隔2秒后第二次向服务器发送数据
    l         第七条记录时服务器的确认信息
    l         第八条记录是在等待2秒后,调用了shutdown,客户端向服务器发送了FIN标志
    l         第九条记录时服务器的确认信息
    l         第十条记录是服务器接收到客户端的一条记录后休眠4秒,发回给客户端
    l         第十一条记录是由于客户端已经通过closesocket关闭了连接,连接已经不存在,因此客户端向服务器发送了RST标志
    总结
           为了保证在连接撤销之前,保证双方都能接收到对等方的所有数据,在调用closesocket之前,先调用shutdown关闭发送数据通道.
    例子代码
     
    客户端代码:
     
    #define WIN32_LEAN_AND_MEAN
    #include <stdlib.h>
    #include <windows.h>
    #include <tchar.h>
    #include <string.h>
    #include <io.h>
    #include<stdlib.h>
    #include<iostream.h>
    #include<conio.h>
    #include<stdio.h>
    #include<winsock2.h>
    #include<windows.h>
    #include <Ws2tcpip.h>
    #include <process.h>
    #include <ws2tcpip.h>
    #include <winbase.h>
    #pragma comment(lib,"ws2_32.lib")
    #pragma comment(lib,"Kernel32.lib")
     
    #define PORTNUM         4500         
    #define HOSTNAME        "localhost"                               
    int main()         
    {
     TCHAR szError[100];                 // Error message string
     SOCKET ServerSock = INVALID_SOCKET;
     SOCKADDR_IN destination_sin;       
     PHOSTENT phostent = NULL;          
                                         
     WSADATA WSAData;                   
                                          
     // Initialize Winsocket.
     if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0)
     {
        wsprintf (szError, TEXT("WSAStartup failed. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        return FALSE;
     }
     if ((ServerSock = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
     {
        wsprintf (szError, TEXT("Allocating socket failed. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        return FALSE;
     }
     destination_sin.sin_family = AF_INET;
     if ((phostent = gethostbyname (HOSTNAME)) == NULL)
     {
        wsprintf (szError, TEXT("Unable to get the host name. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        closesocket (ServerSock);
        return FALSE;
     }
     memcpy ((char FAR *)&(destination_sin.sin_addr), phostent->h_addr, phostent->h_length);
     destination_sin.sin_port = htons (PORTNUM);
     
     if (connect (ServerSock,
                   (PSOCKADDR) &destination_sin,
                   sizeof (destination_sin)) == SOCKET_ERROR)
     {
           DWORD dw = GetLastError();
        closesocket (ServerSock);
        return FALSE;
     }
     
     send(ServerSock,"ABCDEFG", 7, 0);     //第一次发送数据
     Sleep(2000);
     send(ServerSock,"1234567", 7, 0);     //第二次发送数据
     Sleep(2000);
     shutdown (ServerSock, 0x01);          //关闭发送方的连接或者调用closesocket(ServerSock);
     char buffer[100] = {""};
     while(1)
     {
            int rc = recv(ServerSock,buffer,99,0);
            if(rc < 0)
            {
                   fprintf(stderr,"recv error,error code: %d/n",GetLastError());
                   fflush(stderr);
                   break;
            }
            else if( rc == 0)
            {
                   fprintf(stderr,"server disconnected/n");
                   fflush(stderr);
                   break;
            }
            else
            {
                   fputs(buffer,stdout);
                   fputs("/n",stdout);
                   fflush(stdout);
            }
     }
     closesocket (ServerSock);
     WSACleanup ();
     return TRUE;
    }
     
    服务器端代码:
     
    #define WIN32_LEAN_AND_MEAN
    #include <stdlib.h>
    #include <windows.h>
    #include <tchar.h>
    #include <string.h>
    #include <io.h>
    #include<stdlib.h>
    #include<iostream.h>
    #include<conio.h>
    #include<stdio.h>
    #include<winsock2.h>
    #include<windows.h>
    #include <Ws2tcpip.h>
    #include <process.h>
    #include <ws2tcpip.h>
    #include <winbase.h>
    #pragma comment(lib,"ws2_32.lib")
    #pragma comment(lib,"Kernel32.lib")
     
    #define PORTNUM               4500    // Port number 
    #define MAX_PENDING_CONNECTS 4       // Maximum length of the queue
    int main()          
    {       
     
     char szServerA[100] = {0};                // ASCII string
     TCHAR szError[100] = {0};                 // Error message string
     
     SOCKET WinSocket = INVALID_SOCKET, // Window socket
             ClientSock = INVALID_SOCKET; // Socket for communicating
                                          // between the server and client
     SOCKADDR_IN local_sin,              // Local socket address
                  accept_sin;             // Receives the address of the
                                          // connecting entity
     int accept_sin_len;                 // Length of accept_sin
     
     WSADATA WSAData;                    // Contains details of the Winsock
                                          // implementation
     
     // Initialize Winsock.
     if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0)
     {
        wsprintf (szError, TEXT("WSAStartup failed. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        return FALSE;
     }
     
     // Create a TCP/IP socket, WinSocket.
     if ((WinSocket = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
     {
        wsprintf (szError, TEXT("Allocating socket failed. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        return FALSE;
     }
     
     // Fill out the local socket's address information.
     local_sin.sin_family = AF_INET;
     local_sin.sin_port = htons (PORTNUM); 
     local_sin.sin_addr.s_addr = htonl (INADDR_ANY);
     // Associate the local address with WinSocket.
     if (bind (WinSocket,
                (struct sockaddr *) &local_sin,
                sizeof (local_sin)) == SOCKET_ERROR)
     {
        wsprintf (szError, TEXT("Binding socket failed. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        closesocket (WinSocket);
        return FALSE;
     }
     
     // Establish a socket to listen for incoming connections.
     if (listen (WinSocket, MAX_PENDING_CONNECTS) == SOCKET_ERROR)
     {
        wsprintf (szError,
                  TEXT("Listening to the client failed. Error: %d"),
                  WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
        closesocket (WinSocket);
        return FALSE;
     }
     accept_sin_len = sizeof (accept_sin);
     // Accept an incoming connection attempt on WinSocket.
     ClientSock = accept (WinSocket,
                           (struct sockaddr *) &accept_sin,
                           (int *) &accept_sin_len);
     
     if (ClientSock == INVALID_SOCKET)
     {
        wsprintf (szError, TEXT("Accepting connection with client failed.")
                  TEXT(" Error: %d"), WSAGetLastError ());
        MessageBox (NULL, szError, TEXT("Error"), MB_OK);
           closesocket (WinSocket);
        return FALSE;
     }
     char RecvBuf[100] = {""};
     int lReadCount = 100;
     while(1)
     {
                  int size = sizeof(RecvBuf);
                  int nRes = recv(ClientSock,RecvBuf,lReadCount,0);
                  if(nRes == 0)
                  {
                         fprintf(stderr,"client disconnected /n");
                         break;
                  }
                  else if( nRes < 0)
                  {
                         fprintf(stderr,"recv failed");
                         break;
                  }
                  printf("Recv: data: %s datalen: %d /r/n",RecvBuf,nRes);
          
                  Sleep(4000);
                 
                  nRes = send(ClientSock,RecvBuf,nRes,0);
                  if(nRes <= 0)
                  {
                         fprintf(stderr,"send failed,error code: %d/r/n",errno);
                  }
                  memset(RecvBuf,NULL,lReadCount);
     }
     
     
     // Close ClientSock.
     closesocket (ClientSock);
     WSACleanup ();
     return TRUE;
    }
  • 相关阅读:
    写给可能的你我他~ 确诊抑郁后的我
    nginx+lua (二)请求分发
    nginx+lua (一)
    部署openresty
    spring-data-redis 自定义缓存(@annotation+lua+aop)(一)
    基于RESTful实现 图片验证码
    HoloCircularProgressBar 使用
    I/O 多路转接 select
    Android中Activity的生命周期
    硬件访问服务AIDL HAL
  • 原文地址:https://www.cnblogs.com/invisible2/p/6650227.html
Copyright © 2020-2023  润新知