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

原创 2007年09月12日 17:02:00
  
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;
}
 
  

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

理解tcp顺序释放操作和tcp的半关闭 时间: 2008.12.30 13:56:00 标签: Shutdown的调用 在关闭socket的时候,可以有两种方式closesocket和shutdown...
  • lionzl
  • lionzl
  • 2009年03月21日 16:51
  • 1196

基于TCP的半关闭

基于TCP的半关闭 TCP练级的半关闭简而言之就是”关闭连接的一半”(只可以传递或接收数据)   套接字和流 两台主机通过套接字建立连接后进入可交换数据的状态(流形参的状态),即将建立套接字后可交换数...
  • Hello_World_LVLcoder
  • Hello_World_LVLcoder
  • 2016年12月22日 07:17
  • 644

TCP半关闭

关闭TCP连接       http://book.51cto.com/art/200902/109775.htm TCP/IP学习笔记(六)       http://www.qqread.co...
  • zxg519
  • zxg519
  • 2014年01月15日 12:06
  • 746

tcp listen和accept过程

详细看此贴: http://topic.csdn.net/u/20110619/19/53181BF0-79F5-4F86-889F-927EE80233DD.html listen函...
  • klarclm
  • klarclm
  • 2012年07月25日 11:54
  • 4403

Linux下的TCP/IP编程------基于TCP的半关闭

在TCP服务端和客户端建立连接之后服务端和客户端会分别有两个独立的输入流和输出流,而且相互对应。服务端的输出流对应于客户端的输入流,服务端的输入流对应于客户端的输出流在Linux下的close()函数...
  • wqc_CSDN
  • wqc_CSDN
  • 2016年05月31日 11:27
  • 1266

深入浅出TCP之半关闭与CLOSE_WAIT

终止一个连接要经过4次握手。这由TCP的半关闭(half-close)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递,可理解为两个方向相反的独立通道),因此每个方向必须单独地进行关闭...
  • realmeh
  • realmeh
  • 2014年01月13日 23:32
  • 3564

TCP-IP详解:TCP半打开连接及同时打开同时关闭

TCP半打开连接 如果一方已经关闭或者异常终止连接而另外一方却还不知道,这样的连接就称为半打开连接(Half open connection)。处于半打开的连接,如果双方不进行数据通信,是发现不了...
  • wdscq1234
  • wdscq1234
  • 2016年09月03日 14:23
  • 1674

TCP半关闭状态

当TCP链接中A发送FIN请求关闭,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A时,A方处在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。  ...
  • gjggj
  • gjggj
  • 2017年06月26日 15:19
  • 361

半连接、半打开、半关闭

一、半连接 1.1 定义       发生在TCP3次握手中。       如果A向B发起TCP请求,B也按照正常情况进行响应了,但是A不进行第3次握手,这就是半连接。 1.2 半连接攻击      ...
  • kanguolaikanguolaik
  • kanguolaikanguolaik
  • 2013年09月17日 11:47
  • 4311

[疯狂Java]TCP:半关闭Socket

1. 怎样表示输入或输出的结束:     1) 都知道TCP通信其实就是用客户端(Socket对象)的socket传递数据的,传递数据都是通过Socket类获取InputStream和OutputS...
  • Lirx_Tech
  • Lirx_Tech
  • 2016年03月24日 22:12
  • 1589
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:理解tcp顺序释放操作和tcp的半关闭
举报原因:
原因补充:

(最多只允许输入30个字)