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;
}
- Wireshark抓包分析TCP建立/释放链接的过程以及状态变迁分析
Wireshark抓包分析TCP建立/释放链接的过程以及状态变迁分析 一.介绍计算机网络体系结构 1.计算机的网络体系结构 在抓包分析TCP建立链接之前首先了解下计算机的网络通信的模型,我相信学习过计 ...
- 为什么 TCP 建立连接是三次握手,关闭连接确是四次挥手呢?
Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:小书go https://blog.csdn.net/qzcsu/article/details/72861891 背 ...
- Linux:TCP状态/半关闭/2MSL/端口复用
TCP状态 CLOSED:表示初始状态. LISTEN:该状态表示服务器端的某个SOCKET处于监听状态,可以接受连接. SYN_SENT:这个状态与SYN_RCVD遥相呼应,当客户端SOCKET执行 ...
- 我为 Netty 贡献源码 | 且看 Netty 如何应对 TCP 连接的正常关闭,异常关闭,半关闭场景
欢迎关注公众号:bin的技术小屋,本文图片加载不出来的话可查看公众号原文 本系列Netty源码解析文章基于 4.1.56.Final版本 写在前面..... 本文是笔者肉眼盯 Bug 系列的第三弹,前 ...
- TCP/IP源码(59)——TCP中的三个接收队列
http://blog.chinaunix.net/uid-23629988-id-3482647.html TCP/IP源码(59)——TCP中的三个接收队列 作者:gfree.wind@gmai ...
- 深入浅出TCP之半关闭与CLOSE_WAIT
转自:https://www.2cto.com/net/201309/243585.html(相关链接) 深入浅出TCP之半关闭与CLOSE_WAIT 终止一个连接要经过4次握手.这由TCP的半关闭( ...
- TCP之半关闭与CLOSE_WAIT
终止一个连接要经过4次握手.这由TCP的半关闭(half-close)造成的.既然一个TCP连接是全双工(即数据在两个方向上能同时传递,可理解为两个方向相反的独立通道),因此每个方向必须单独地进行关闭 ...
- TCP/IP 详解卷一 - TCP CWR、ECE、URG、ACK、PSH、RST、SYN、FIN控制位
from:https://blog.csdn.net/u012243115/article/details/43487461 2015年02月04日 15:56:32 阅读数:1464 TCP 和 U ...
- 图解 TCP/IP 第六章 TCP与UDP 笔记6.1 传输层的作用
图解 TCP/IP 第六章 TCP与UDP 笔记6.1 传输层的作用 传输层必须指出这个具体的程序,为了实现这一功能,使用端口号这样一种识别码.根据端口号,就可以识别在传输层上一层的应用程 ...
随机推荐
- python selenium配置
写该博客时环境 mac 10.14.1 (18B75) python 3.7 pip (不用这个就是了,用pip3) $ pip --version pip 10.0.1 from /Users/wj ...
- haproxy实现会话保持
HAProxy系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html 1.反向代理为什么需要设置cookie 任何一个七层的http负载均衡器,都 ...
- 随着应用对事务完整性和并发性要求的不断提高,MySQL才开始开发基于事务的存储引擎
MYSQL 解锁与锁表 - 专注it - 博客园 https://www.cnblogs.com/wanghuaijun/p/5949934.html 2016-10-11 16:50 MYSQL 解 ...
- 2014年蓝桥杯省赛A组c++第3题(数组构造+暴力求解)
/* 标题:神奇算式 由4个不同的数字,组成的一个乘法算式,它们的乘积仍然由这4个数字组成. 比如: 210 x 6 = 1260 8 x 473 = 3784 27 x 81 = 2187 都符合要 ...
- Page12:镇定条件、镇定与极点配置的关系,解耦控制的概念等[Linear System Theory]
内容包含镇定条件.镇定与极点配置的关系,解耦控制的概念.形式.分类以及各种解耦方法特点,系统能否解耦判断.
- [qemu][kvm] 在一个vmware虚拟机里安装qemu-kvm虚拟机
说起来这个需求,简直是傻傻的.但却实实在在的摆在我的面前.... VM无外乎就是为了模拟场景:我现在要的场景就是一台很多个core的linux主机.但是我只有一个装了windows的笔记本.上边有一个 ...
- 微博登录报错 sso package orsign error
https://blog.csdn.net/gao_chun/article/details/41344725 (1)检查应用包名签名信息是否完善 如果你的应用只有一个包名.签名,请在 http:// ...
- jmeter_用户并发登录
部分摘自:https://blog.csdn.net/weixin_41291554/article/details/80492276 第一种方案:对登录账号和密码进行参数化 1.添加设置线程数: N ...
- 洛谷P4151 最大XOR和路径 [WC2011] 线性基+图论
正解:线性基+图论 解题报告: 传送门 首先可以思考一下有意义的路径会是什么样子,,,那就一定是一条链+一些环 挺显然的因为一条路径原路返回有没有意义辣?所以一定是走一条链+一些环(当然也可以麻油环, ...
- Mybatis批量insert 返回主键值和foreach标签详解
Mybatis批量insert 返回主键 Mybatis从3.3.1版本开始,支持批量插入后返回主键ID.首先对于支持自增主键的数据库使用useGenerateKeys和keyProperty,对于不 ...