前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >关于close和shutdown

关于close和shutdown

作者头像
xcywt
发布于 2022-05-09 06:21:20
发布于 2022-05-09 06:21:20
1.4K00
代码可运行
举报
文章被收录于专栏:xcywtxcywt
运行总次数:0
代码可运行

我们知道TCP是全双工的,可以在接收数据的同时发送数据。 假设有主机A在和主机B通信,可以认为是在两者之间存在两个管道。就像这样: A ---------> B A <--------- B

1.close   close可以用来关闭一个文件描述符。也就可以用来关闭一个套接字。   当关闭一个套接字时,该套接字不能再由调用进程使用。如果调用进程再去read、write就会出错。

  我们知道关闭一个socket描述符时,会给对方发送一个FIN数据段。比如在主机A中close了与主机B通信的sockA。相当于终止了全双工的那两个管道。而从传输层来看,TCP会尝试将目前发送缓冲区中积压的数据发到链路层上,然后才会发起TCP的4次挥手以彻底关闭TCP连接。   之后在主机A中就不能用sockA来接收数据和发送数据了,同时由于是面向连接的。之前与sockA连接的sockB也收不到数据了。   如果依然通过sockB往主机A上写数据,开始会触发一个RST重连包,然后会收到一个SIGPIPE信号。

  但是如果存在父子进程共用socket描述符的时候(比如fork了一个子进程),父子进程都有相同数值的文件描述符,且都是打开的。这时候去关闭父进程中的描述符并不会发送FIN包给对方。只有子进程也关闭了才会发送FIN。

  原因在于,fork时,父子进程共享着套接字,套接字描述符的引用计数记录着共享着的进程个数。fork一次时相当于引用计数为2了。这时候去关闭一个,只会让引用计数减一。只有当引用计数为0时(也就是子进程也close了),才会发送FIN给连接方。   (就有点像windows下的句柄handle,是一个内核对象,当每被打开一次时,引用计数就会加一,CloseHandle时引用计数减一,若引用计数为0时,操作系统会回收这个内核对象)

2.shutdown 也可以用来关闭TCP数据传输的一个或两个方向。 原型:

SYNOPSIS        #include <sys/socket.h>        int shutdown(int sockfd, int how); DESCRIPTION        The  shutdown()  call causes all or part of a full-duplex connection on        the socket associated with sockfd to be shut down.  If how is  SHUT_RD,        further  receptions  will  be  disallowed.   If how is SHUT_WR, further        transmissions will be disallowed.  If how is SHUT_RDWR, further  recep‐        tions and transmissions will be disallowed. RETURN VALUE        On  success,  zero is returned.  On error, -1 is returned, and errno is        set appropriately.

参数:第一个表示socket描述符 第二个表示关闭读还是写。具体有三个值: 1)SHUT_WR:关闭读,表示不能用第一个参数对应的描述符往管道里面写数据了。(但是依然可以写数据) 2)SHUT_RD:关闭写,不能写数据了。(依然可以接收数据) 3)SHUT_RDWR:同时关闭读和写

3.close和shutdown的区别 1)close只会让引用计数减一,只有在引用计数减为零的时候才会给对方发送FIN段来断开连接。而shutdown会直接关闭连接,不受引用计数的限制,这就意味着在多进程中,只有调用了这个关闭了写端,那么其他进程也都不能写了。 2)close会关闭两端,shutdown可以选择关闭某个端。(这点非常有用处,比如主机A和B正在通信,A觉得没数据发送了,想要断开连接。然后A调用了close,那么B的数据也将发不过来,但是可以选择用shutdown关闭写端,这时候可以接收完B发的数据)

4.实例,用于更好的分析理解shutdown的机制: client从标准输入中接收数据发送给server。server用来接收client的数据,并且回射回去。 这里做一个处理,client发送一次数据之后马上按下Ctrl+D(会导致fgets返回NULL),然后shutdown写端(相当于往server发送了FIN段)。server收到数据后,sleep10s再回射回去。 具体关于下面代码的理解可以参考:http://www.cnblogs.com/xcywt/p/8087677.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<signal.h>

#define CLIENTCOUNT 100

void sig_recvpipe(int sig)
{
    printf("recv pipe = %d\n", sig);
}

int main(int argc, char **argv)
{
    signal(SIGPIPE, sig_recvpipe);
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0)
    {
        perror("socket");
        return -1;
    }
    
    unsigned short sport = 8080;
    if(argc == 2)
    {
        sport = atoi(argv[1]);
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    printf("port = %d\n", sport);
    addr.sin_port = htons(sport);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if(bind(listenfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        return -2;
    }

    if(listen(listenfd, 20) < 0)
    {
        perror("listen");
        return -3;
    }
        
    struct sockaddr_in connaddr;
    int len = sizeof(connaddr);
    
    int i = 0, ret = 0;
    int client[CLIENTCOUNT];
    for(i = 0; i<CLIENTCOUNT; i++)
        client[i] = -1;

    fd_set rset;
    fd_set allset;
    FD_ZERO(&rset);    
    FD_ZERO(&allset);    
    FD_SET(listenfd, &allset);
    int maxfd = listenfd;
    int nready = 0;
    char buf[1024] = {0};
    while(1)
    {
        rset = allset;
        nready = select(maxfd+1, &rset, NULL, NULL, NULL);
        if(nready == -1)
        {
            perror("select");
                        return -3;

        }
        if(nready == 0)
        {
            continue;
        }

        if(FD_ISSET(listenfd, &rset))
        {
            int conn = accept(listenfd, (struct sockaddr*)&connaddr, &len);
            if(conn < 0)
            {
                perror("accept");
                return -4;
            }
        
            char strip[64] = {0};
            char *ip = inet_ntoa(connaddr.sin_addr);
            strcpy(strip, ip);
            printf("new client connect, conn:%d,ip:%s, port:%d\n", conn, strip,ntohs(connaddr.sin_port));
            
            FD_SET(conn, &allset);
            if(maxfd < conn) // update maxfd
                maxfd = conn;    

            int i = 0;
            for(i = 0; i<CLIENTCOUNT; i++)
            {
                if(client[i] == -1)
                {
                    client[i] = conn;
                    break;
                }
            }
            if(i == CLIENTCOUNT)
            {
                printf("to many client connect\n");
                exit(0);
            }
            
            if(--nready <= 0)
                continue;
        }
        for(i = 0; i < CLIENTCOUNT; i++)
        {
            if(client[i] == -1)
                continue;
            if(FD_ISSET(client[i], &rset))
            {
                ret = read(client[i], buf, sizeof(buf));
                if(ret == -1)
                {
                    perror("read");
                    return -4;
                }
                else if(ret == 0)
                {
                    printf("client close remove:%d\n", client[i]);
                    FD_CLR(client[i], &allset);
                    close(client[i]);
                    client[i] = -1;  // 要在这里移除
                }
                
                // fputs(buf, stdout);
                printf("Recv client%d:%s", client[i], buf);
                sleep(10);
                write(client[i], buf, sizeof(buf));
                memset(buf, 0, sizeof(buf));

                if(--nready <= 0)
                    continue;
            }
        }        
    }

    close(listenfd);
    return 0;
}

client端:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>

#include<stdlib.h>
#include<stdio.h>
#include<string.h>

void select_test(int conn)
{
    int ret = 0;
    fd_set rset;
    FD_ZERO(&rset);

    int nready;
    int maxfd = conn;
    int fd_stdin = fileno(stdin);
    if(fd_stdin > maxfd)
    {
        maxfd = fd_stdin;
    }
    
    int stdinoff = 0;
    int len = 0;
    char readbuf[1024] = {0};
    char writebuf[1024] = {0};
    while(1)
    {
        FD_ZERO(&rset);
        if(!stdinoff)
            FD_SET(fd_stdin, &rset);
        FD_SET(conn, &rset);
        nready = select(maxfd+1, &rset, NULL, NULL, NULL);
        if(nready == -1)
        {
            perror("select");
            exit(0);
        }
        else if(nready == 0)
        {
            continue;    
        }

        if(FD_ISSET(conn, &rset))
        {
            ret = read(conn, readbuf, sizeof(readbuf));
            if(ret == 0)
            {
                printf("server close1\n");
                break;
            }
            else if(-1 == ret)
            {
                perror("read1");
                break;
            }    

            fputs(readbuf, stdout);
            memset(readbuf, 0, sizeof(readbuf));
        }    
        
        if(FD_ISSET(fd_stdin, &rset))
        {
            if(fgets(writebuf, sizeof(writebuf), stdin) == NULL)
            {
                #if 0
                printf("After 5s client exit\n");
                close(conn);
                sleep(5);
                exit(EXIT_FAILURE);
                #else
                shutdown(conn, SHUT_WR);
                stdinoff = 1;
                #endif
            }
            else
            {
                write(conn, writebuf, sizeof(writebuf));
                memset(writebuf, 0, sizeof(writebuf));    
            }
        }
    }
    close(conn);
}

int sockfd = 0;
int main(int argc, char **argv)
{
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket");
        return -1;
    }
        
    unsigned short sport = 8080;
    if(argc == 2)
    {
        sport = atoi(argv[1]);
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    printf("port = %d\n", sport);
    addr.sin_port = htons(sport);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    {
        perror("connect");
        return -2;
    }

    struct sockaddr_in addr2;
    socklen_t len = sizeof(addr2);
    if(getpeername(sockfd, (struct sockaddr*)&addr2, &len) < 0)
    {
        perror("getsockname");
        return -3;
    }

    printf("Server: port:%d, ip:%s\n", ntohs(addr2.sin_port), inet_ntoa(addr2.sin_addr));

    select_test(sockfd);

    close(sockfd);
    return 0;
}

编译运行: makefile:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CC=gcc
CFLAGS=-Wall -g
LIBS=-lpthread
all:echoser echocli
echoser:server.c
    $(CC) $< $(CFLAGS) $(LIBS) -o $@
echocli:client.c
    $(CC) $< $(CFLAGS) $(LIBS) -o $@
.PHONY:clean
clean:
    rm -f *.o echoser echocli *~

client端运行: 在发送完1111111时马上按下Ctrl+d,将读端关闭。然后会发现10s以后还是可以收到server的数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xcy@xcy-virtual-machine:~/test/sock8_shutdown$ ./echocli 
port = 8080
Server: port:8080, ip:127.0.0.1
11111111
11111111
server close1
xcy@xcy-virtual-machine:~/test/sock8_shutdown$

server端运行: server收到数据,10s后发送给client。之后还会read返回0,会认为是client关闭了,然后就把套接字关闭了。最后client也能收到read返回0。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xcy@xcy-virtual-machine:~/test/sock8_shutdown$ ./echoser 
port = 8080
new client connect, conn:4,ip:127.0.0.1, port:55852
Recv client4:11111111
client close remove:4
^C
xcy@xcy-virtual-machine:~/test/sock8_shutdown$

查看TCP状态: 当按下Ctrl+d时去查看状态,下面第2行可以看出来client已经变成CLOSE_WAIT状态了,server变成了FIN_WAIT2. 等10s到了,再去看client变成了TIME_WAIT状态(要保持2MSL) 具体可以参考:十一种状态 http://www.cnblogs.com/xcywt/p/8082428.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xcy@xcy-virtual-machine:~$ netstat -an | grep 8080
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN     
tcp        1      0 127.0.0.1:8080          127.0.0.1:55852         CLOSE_WAIT 
tcp        0      0 127.0.0.1:55852         127.0.0.1:8080          FIN_WAIT2  
xcy@xcy-virtual-machine:~$ netstat -an | grep 8080
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:55852         127.0.0.1:8080          TIME_WAIT  
xcy@xcy-virtual-machine:~$ netstat -an | grep 8080
xcy@xcy-virtual-machine:~$

我们可以看出来,client可以关闭写端,但是还是可以接收到到server发来的数据。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-12-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
创建 REST 服务简介
REST 命名自“Representational State Transfer”,具有以下属性:
用户7741497
2022/08/04
5930
修改规范(SPEC)类
默认情况下,编译器使用操作的 operationId 来确定相应 REST 调用调用的方法的名称。可以指定不同的名称。为此,请将以下内容添加到规范类的 OpenAPI XData 块中的操作中:
用户7741497
2022/08/05
7690
RPC 与 REST 介绍及比较
当下微服务架构都面临着服务间的远程调用,常见的远程调用方式有两种: RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型。 Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。现在热门的Rest风格,就可以通过http协议来实现。
Freedom123
2024/03/29
7610
[解读REST] 6.REST的应用经验以及教训
衔接上文[解读REST] 5.Web的需求 & 推导REST,上文根据Web的需求推导出了REST架构风格,以及REST的详细描述和解释。自从1994年以来,REST架构风格被用于指导Web架构的设计和开发工作,最重要的两点体现是在设计HTTP和URI两个互联网规范协议中,以及实现这些规范的libwww-perl客户端库,Apache HTTP项目(httpd)以及其他的实现中,所得到的经验以及教训。 其实REST也用于指导约束超媒体的设计工作,比如HTML,但是Fielding并未在论文中详细解释这部分
blackheart
2018/01/19
1.1K0
API架构风格的深度解析与选择策略:SOAP、REST、GraphQL与RPC
API作为系统间通信的桥梁,其设计风格也在持续发展和完善。SOAP、REST、GraphQL和RPC作为四种主流的API架构风格,各自具有鲜明的特点和适用场景。
公众号:码到三十五
2024/12/09
1630
API架构风格的深度解析与选择策略:SOAP、REST、GraphQL与RPC
修改实现(IMPL)类
对于实现类中的每个方法,根据使用它的 REST 调用编辑方法定义(特别是实现)。请注意,该方法前面有一个注释,该注释是相应 REST 调用描述的副本。在实施中:
用户7741497
2022/08/05
3710
We Do Sleep At Night, We Do REST Right
前言 笔者在上一篇文章中提过,任何一种非“强制性”约束同时也没有“标杆”工具支持的开发风格或协议,最后都会在不同的程序员手中得到不同的诠释,微服务是如此,DDD 是如此,笔者把它称为技术思想上的“康威定律”。不出意外的,REST 同样难逃此劫。光是在学习和收集资料的过程中,笔者就已经见过不下十多篇此类理解,甚至于在 url 中使用短划线或下划线连接单词也是众口难调。 尽管这只是小事。 微软也发布过关于如何设计 REST API 的开发指南,但是不幸的是,REST 的创始人 Roy Fielding 认为微
潘成涛
2018/07/09
9510
REST 服务安全
如果 REST 服务正在访问机密数据,应该对服务使用身份验证。如果需要为不同的用户提供不同级别的访问权限,还要指定端点所需的权限。
用户7741497
2022/08/05
9320
使用 /api/mgmnt/ 服务
/api/mgmnt 服务还提供了可用于发现和记录 Web 服务的选项,如本书后面所述。
用户7741497
2022/08/04
5830
在 REST 服务中支持 CORS
本节提供 CORS 的概述以及如何在 IRIS REST 服务中启用 CORS 的概述。
用户7741497
2022/08/05
2.6K0
什么是REST架构?
REST架构风格是全新的针对Web应用的开发风格,是当今世界最成功的互联网超媒体分布式系统架构,它使得人们真正理解了Http协议本来面貌。随着 REST架构成为主流技术,一种全新的互联网网络应用开发的思维方式开始流行。
java思维导图
2018/10/08
7990
[解读REST] 5.Web的需求 & 推导REST
衔接上文[解读REST] 4.基于网络应用的架构风格,上文总结了一些适用于基于网络应用的架构风格,以及其评估结果。在前文的基础上,本文介绍一下Web架构的需求,以及在对Web的关键协议进行设计和改进的过程中遇到的问题;以及在对基于网络应用的架构风格进行评估的过程中的领悟;结合Web的需求进而推导出REST这种架构风格,随后使用REST来指导Web架构的设计和改进工作。 1 Web的需求 在本系列博客的第一篇博客[解读REST] 1.REST的起源中,Web之父Berners-Lee在世界上第一个网站写下的第
blackheart
2018/01/19
8030
[解读REST] 5.Web的需求 & 推导REST
发现和记录 REST API
/api/mgmnt 服务包括可用于发现 REST 服务类和启用 REST 的 Web 应用程序的调用。
用户7741497
2022/08/05
7210
Asp.NetCore Web开发之会话技术
这节讲一下会话技术,首先了解一下什么是会话,会话是指浏览器打开到关闭的过程中,多次与服务器发送接收数据的过程。
宿春磊Charles
2022/03/29
6330
Asp.NetCore Web开发之会话技术
如何理解 CRUD 与 REST
CRUD 和 REST 是应用开发领域中两个比较常见的概念,但由于二者之间概念存在重叠而常常被混淆。简单来说,REST 是一种软件架构风格,是一种针对网络应用的设计和开发方式。而 CRUD 是一个缩写,指的是数据库中可以执行的四种基本操作:创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete)。
码匠Majiang
2023/01/03
9420
如何理解 CRUD 与 REST
【ASP.NET Core 基础知识】--Web API--RESTful设计原则
RESTful设计的背景源于Roy Fielding博士在他2000年的博士论文中提出的REST(Representational State Transfer)架构风格。REST旨在构建可伸缩、可维护的网络应用,强调资源的统一标识、无状态通信和统一接口。基于HTTP协议,RESTful设计通过简化架构、提高系统可靠性,促使Web服务的广泛应用。
喵叔
2024/05/24
2030
那些年,我们一起误解过的REST
最近几年REST API越来越流行,特别是随着微服务的概念被广泛接受和应用,很多Web Service都使用了REST API。
Coder Sam
2018/08/27
2.1K0
那些年,我们一起误解过的REST
RPC 和 REST还有RESTFul到底是个什么玩意?
直接在QQ获取ID为123456的用户。 2. 对资源的操作包括获取、创建、修改和删除,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。例如:我们在使用CSDN的时候,在上传文章的时候REST规范就是你后台提供的API是使用的是HTTP 中是的POST方法。在删除文章的时候使用DELETE方法。
袁新栋-jeff.yuan
2020/08/26
4.2K0
RPC 和 REST还有RESTFul到底是个什么玩意?
用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 (2) + 准备项目
上一部分预备知识在这 http://www.cnblogs.com/cgzl/p/9010978.html 如果您对ASP.NET Core很了解的话,可以不看本文, 本文基本都是官方文档的内容。 A
solenovex
2018/05/30
1.1K0
REST In WCF4.0
REST软件架构是由Roy Thomas Fielding博士2000年在他的论文《Architectural Styles and the Design of Network- based Software Architectures》首次提出的。他提出的理论对后来的Web技术的发展产生了巨大的影响,他是许多重要Web架构标准的设计者,这些标准就是HTTP、URI等。 1.1) Rest的英文全称是“Representational State Transfer”。中文翻译为“表述性状态转移”。RES
张善友
2018/01/30
5810
相关推荐
创建 REST 服务简介
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文