close:
当套接字的引用计数为0的时候才会引发TCP的四分组连接终止序列;
shutdown:
不用管套接字的引用计数就激发TCP的正常连接终止序列;
这里由一个SO_LINGER套接字选项
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time,POSIX specifies units as seconds */
};
shutdown:SHUT_RD
关闭连接的读这一半,进程不能再对这样的套接字调用任何读操作;
在套接字上不能再发出接收请求,进程仍可往套接字发送数据,套接字接收缓冲区中所有数据被丢弃,再接收到的任何数据由TCP丢弃,对套接字发送缓冲区没有任何影响;
程序模拟:
client:
struct sockaddr_in serverAdd;
bzero(&serverAdd, sizeof(serverAdd));
serverAdd.sin_family = AF_INET;
serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
serverAdd.sin_port = htons(SERV_PORT);
int connfd = socket(AF_INET, SOCK_STREAM, 0);
int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
if (connResult < 0) {
printf("连接失败\n");
close(connfd);
return;
}
else
{
printf("连接成功\n");
}
ssize_t writeLen;
char sendMsg[5000] = {0};
unsigned long long totalSize = 0;
if (1) {
writeLen = write(connfd, sendMsg, sizeof(sendMsg));
if (writeLen < 0) {
printf("发送失败 errno = %d\n",errno);
return;
}
else
{
totalSize += writeLen;
printf("发送成功 totalSize = %zd\n",totalSize);
}
sleep(20);
}
#define SERV_PORT 8000
int main(int argc, const char * argv[])
{
struct sockaddr_in serverAdd;
struct sockaddr_in clientAdd;
bzero(&serverAdd, sizeof(serverAdd));
serverAdd.sin_family = AF_INET;
serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
serverAdd.sin_port = htons(SERV_PORT);
socklen_t clientAddrLen;
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
setsockopt(listenfd,
SOL_SOCKET, SO_REUSEADDR,
(void *)&yes, sizeof(yes));
if (listenfd < 0) {
printf("创建socket失败\n");
return -1;
}
int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
if (bindResult < 0) {
printf("绑定端口失败\n");
close(listenfd);
return -1;
}
listen(listenfd, 20);
int connfd;
unsigned char recvMsg[246988];
unsigned long long totalSize = 0;
clientAddrLen = sizeof(clientAdd);
connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
if (connfd < 0) {
printf("连接失败\n");
return -1;
}
else{
// 这里我们用于测试,只接收一个连接
close(listenfd);
}
shutdown(connfd,SHUT_RD);
while (1) {
ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
if (readLen < 0) {
printf("读取失败 errno = %d\n",errno);
close(connfd);
return -1;
}
else if (readLen == 0) {
printf("读取完成 totalSize = %llu\n",totalSize);
close(connfd);
return -1;
}
else
{
totalSize += readLen;
printf("readLen:%ld\n",readLen);
}
}
return 0;
}
client:
连接成功
发送成功 totalSize = 5000
server:读取完成 totalSize = 0
是因为读关闭了,套接字接收缓冲区的数据直接被丢弃掉了,如果将服务端的shutdiwn替换成close会如下
读取失败 errno = 9
#define EBADF 9 /* Bad file descriptor */
说明:如果服务端调用shutdown关闭读之后,客户端再往已经收到RST分节的服务端进行write操作,会引发错误导致程序终止,RST详解里面有讲到