Linux socket编程(8):shutdown和close的区别详解及例子

在Linux中有两种操作可以终止socket间的进程通信:closeshutdown。但这两种函数在使用时有着不同的行为和效果。在网络编程中,正确地选择和使用这些操作至关重要,因为它们直接影响着通信的结束和资源的释放。本文将介绍closeshutdown函数,然后举一个实际的例子来说明shutdown的使用。

文章目录

  • 1 close
  • 2 shutdown
  • 3 实例:shutdown的使用
    • 3.1 代码修改
    • 3.2 实验结果
    • 3.3 完整代码

1 close

close函数将终止一个套接字的连接(如果已连接上),然后关闭文件描述符,释放相关的资源。close将关闭数据传输的两个方向,之后进程将无法读/写入套接字。

int close(int sockfd);
  • 参数:
    • sockfd:套接字文件描述符。
  • 返回值:
    • 成功:0
    • 失败:-1,错误号存储于errno中。

注意,close只是将套接字引用计数减一,当引用计数变为零时,真正关闭套接字

  1. 套接字引用计数: 在Linux中,每个打开的套接字都有一个引用计数。引用计数跟踪着对套接字的引用次数,即有多少个文件描述符指向同一个套接字。
  2. close 函数的作用: 当调用close函数关闭一个套接字时,实际上只是将套接字的引用计数减一。这意味着释放了一个对套接字的引用,但套接字仍然存在。

这种机制允许多个进程或线程共享同一个套接字,这个在利用fork实现服务端与多个客户端建立连接中有出现过,使用fork创建子进程的时候,父进程的上下文被拷贝,所以在子进程中的一开始就要close父进程的socket,否则父进程close自身套接字时,套接字并不能真正的被回收。

2 shutdown

shutdown函数用于关闭一个已连接的套接字或禁止在套接字上的发送或接收数据。该函数可以在全双工(TCP)套接字上单独关闭读取或写入,也可以同时关闭两者。

int shutdown(int sockfd, int how);
  • 参数:
    • sockfd:套接字文件描述符。
    • how:关闭方式,可以取值为:
      • SHUT_RD:关闭读取
      • SHUT_WR:关闭写入
      • SHUT_RDWR:同时关闭读取和写入
  • 返回值:
    • 成功:0
    • 失败:-1,错误号存储于 errno 中。

3 实例:shutdown的使用

参考代码上一节的代码IO复用模型之select原理及例子:客户端接收stdin的输入然后发给服务端,服务端收到这个消息后再回显给客户端。

3.1 代码修改

目的:在客户端发送消息给服务端后,等待服务端返回回显数据后,客户端就退出程序。

1、客户端

首先我们希望在标准输入收到EOF时,客户端关闭写方向和标准输入的接收,等待服务端的最后一次回复,收到后打印出来。

在这里插入图片描述

2、服务端

服务端在收到消息后,延时5s,此时在客户端输入EOF,这时客户端关闭掉了写方向。等到延时结束后看看send的回显能不能被客户端收到。

在这里插入图片描述

3.2 实验结果

在这里插入图片描述

这样就实现了客户端发送最后一个消息后,等待服务端回复完后就自动退出程序。

3.3 完整代码

服务端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024

int main() {
    int serverSocket, clientSockets[MAX_CLIENTS], maxSockets, activity, i, valread;
    int opt = 1;
    struct sockaddr_in address;
    fd_set readfds;
    char buffer[BUFFER_SIZE];

    // Create server socket
    if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // Set socket options
    if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("Setsockopt failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8888);

    // Bind the socket
    if (bind(serverSocket, (struct sockaddr*)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // Listen for incoming connections
    if (listen(serverSocket, MAX_CLIENTS) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port 8888\n");

    maxSockets = serverSocket;
    memset(clientSockets, 0, sizeof(clientSockets));

    while (1) {
        FD_ZERO(&readfds);
        FD_SET(serverSocket, &readfds);

        for (i = 0; i < MAX_CLIENTS; i++) {
            int clientSocket = clientSockets[i];
            if (clientSocket > 0) {
                FD_SET(clientSocket, &readfds);
                if (clientSocket > maxSockets) {
                    maxSockets = clientSocket;
                }
            }
        }

        activity = select(maxSockets + 1, &readfds, NULL, NULL, NULL);

        if (FD_ISSET(serverSocket, &readfds)) {
            // Handle new connection
            int newSocket;
            socklen_t addrlen = sizeof(address);
            if ((newSocket = accept(serverSocket, (struct sockaddr*)&address, &addrlen)) < 0) {
                perror("Accept failed");
                exit(EXIT_FAILURE);
            }

            printf("New connection, socket fd is %d, ip is : %s, port : %d\n", newSocket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));

            for (i = 0; i < MAX_CLIENTS; i++) {
                if (clientSockets[i] == 0) {
                    clientSockets[i] = newSocket;
                    break;
                }
            }
        }

        for (i = 0; i < MAX_CLIENTS; i++) {
            int clientSocket = clientSockets[i];
            if (FD_ISSET(clientSocket, &readfds)) {
                // Handle data from client
                valread = read(clientSocket, buffer, BUFFER_SIZE);
                if (valread == 0) {
                    // Client disconnected
                    printf("Host disconnected, socket fd is %d\n", clientSocket);
                    close(clientSocket);
                    clientSockets[i] = 0;
                } else {
                    // Echo received message back to client
                    buffer[valread] = '\0';
                    printf("Received: %s", buffer);
					sleep(5);
					printf("sleep over\n");
                    send(clientSocket, buffer, strlen(buffer), 0);
                }
            }
        }
    }

    return 0;
}

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUFFER_SIZE 1024

int main() {
    int clientSocket;
    struct sockaddr_in serverAddress;
    fd_set readfds;
    char buffer[BUFFER_SIZE];

    // Create client socket
    if ((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(8888);
	serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");

    // Connect to server
    if (connect(clientSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {
        perror("Connection Failed");
        exit(EXIT_FAILURE);
    }

    printf("Connected to server\n");
	int stdinOn = 1;
    while (1) {
        FD_ZERO(&readfds);
		if(stdinOn)
	        FD_SET(STDIN_FILENO, &readfds);
        FD_SET(clientSocket, &readfds);

        select(clientSocket + 1, &readfds, NULL, NULL, NULL);

        if (FD_ISSET(STDIN_FILENO, &readfds)) {
            // Read from stdin and send to server
            if(fgets(buffer, BUFFER_SIZE, stdin) == NULL)
			{
				printf("shutdown\n");
				shutdown(clientSocket, SHUT_WR);
				stdinOn = 0;
			}else
			{
				send(clientSocket, buffer, strlen(buffer), 0);
			}
        }

        if (FD_ISSET(clientSocket, &readfds)) {
            // Read from server and print
            memset(buffer, 0, sizeof(buffer));
            int len = recv(clientSocket, buffer, BUFFER_SIZE, 0);
			if(len == 0)
			{
				printf("server close\n");
				break;
			}
            printf("Server: %s", buffer);
        }
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/202073.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Thrift RPC Java、Go、PHP使用例子

文章目录 1、Thrift RPC介绍1.1、Protocol 支持的数据传输协议1.2、Transport 支持的数据传输方式1.3、Server 支持的服务模型1.4、IDL语法数据类型1.5、开发步骤 2、接口定义文件2.1、创建接口定义文件2.2、生成对应平台语言代码2.2.1、下载生成工具2.2.2、生成各平台语言代码…

单片机----串行通信

目录 串行通信的两种方式 串行通信的传输模式 串行通信的错误校验 1.奇偶校验 2.代码和校验 3.循环冗余码校验 串行口结构 串行口控制寄存器SCON 特殊功能寄存器PCON 串行口的4种工作方式 方式0&#xff1a; &#xff08;1&#xff09;方式0的发送过程 &#xff0…

数据结构---堆

1.堆的概念及结构 堆的性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值堆总是一棵完全二叉树 2.举例说明 堆一般是把数组数据看做是一棵完全二叉树 小堆要求&#xff1a;任意一个父亲<孩子大堆要求&#xff1a;任意一个父亲>孩子 比如&#xff1…

计算机图形学:直线的扫描转换算法解析与实现

直线的扫描转换&#xff1a; DDA算法&#xff1a; 推理&#xff1a; 在计算机显示图形时&#xff0c;由于显示计算机的分辨率是有限的所以我们在绘制图形时需要将图形从连续量转换成离散量才能完成图形的绘制&#xff0c;直线的扫描转换就是将连续量转换为离散量的过程。 对…

UE Web Remote Control

前言 最近在研究UE自启WEB服务和网页通信以此来通过网页与UE进行数据交互&#xff0c;这样最好的方式就是可以摒弃掉整个繁琐的通信连接流程如TCP UDP&#xff0c;但是找到的一些方法都不是很适用&#xff0c;尤其是WEBUI这个插件它只适合内嵌到UE本身才能完成交互&#xff0c;…

mybatis关于namespace以及id以及Mapper接口命名的说明(了解)

1、建库建表 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_emp(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id) );INSERT INTO t_emp(emp_name,emp_salary) VALUES("tom",200.33); INSERT INTO…

Pytorch从零开始实战11

Pytorch从零开始实战——ResNet-50V2算法实战 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——ResNet-50V2算法实战环境准备数据集模型选择开始训练可视化总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;Pyt…

CentOS 8 安装国内、本地YUM源

环境&#xff1a;windows 11、 VMware 17、Cent OS 8 目的&#xff1a;加快软件下载速度 1、国内YUM源安装 使用国外的源&#xff0c;速度卡到不显示 备份默认YUM源文件 [rootlocalhost ~]# cd /etc/yum.repos.d/ [rootlocalhost yum.repos.d]# mkdir yum.bak [rootlocalho…

Drawer抽屉(antd-design组件库)简单用法

1.Drawer抽屉 屏幕边缘滑出的浮层面板。 2.何时使用 抽屉从父窗体边缘滑入&#xff0c;覆盖住部分父窗体内容。用户在抽屉内操作时不必离开当前任务&#xff0c;操作完成后&#xff0c;可以平滑地回到原任务。 需要一个附加的面板来控制父窗体内容&#xff0c;这个面板在需要时…

3D场景建模工具

在线工具推荐&#xff1a; 三维数字孪生场景工具 - GLTF/GLB在线编辑器 - Three.js AI自动纹理化开发 - YOLO 虚幻合成数据生成器 - 3D模型在线转换 - 3D模型预览图生成服务 1. 什么是3D场景建模&#xff1f; 3D场景建模是一种通过计算机图形学技术&#xff0c;将现实世…

<Linux>冯诺依曼体系结构||操作系统||系统调用于用户操作接口

前言:本文从软硬件角度计算机解释软硬件结构 硬件—冯诺依曼体系结构 软件—操作系统 文章目录 冯诺依曼计算机体系结构背景理解举例 操作系统(OS)OS的管理为什么要有操作系统? 系统调用与用户操作接口系统调用用户操作接口引入:printf&&scanf的重新理解库函数 计算机…

Could NOT find resource [logback-test.xml]

修改 之后就可以正常启动了

Node.js案例 - 记账本

目录 项目效果 项目的搭建 ​编辑 响应静态网页 ​编辑 ​编辑 结合MongoDB数据库 结合API接口 进行会话控制 项目效果 该案例实现账单的添加删除查看&#xff0c;用户的登录注册。功能比较简单&#xff0c;但是案例主要是使用前段时间学习的知识进行实现的&#xff0c…

详解原生Spring当中的额外功能开发MethodBeforeAdvice与MethodInterceptor接口!

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

软件测试测试文档的编写和阅读

在软件测试中的流程中&#xff0c;测试文档也是一个重要的流程&#xff0c;所以测试人员也需要学习测试文档的编写和阅读。 一、定义&#xff1a; 测试文档&#xff08;Testing Documentation&#xff09;记录和描述了整个测试流程&#xff0c;它是整个测试活动中非常重要的文…

layui提示框没有渲染bug解决

bug&#xff1a;使用layui时或许是依赖导入又或是ideal和浏览器缓存问题导致前面明明正常的页面显示&#xff0c;后面出现提示框没有css样式&#xff0c;弹出框没有背景css 效果如下 解决后 解决方法 在你的代码中引入layer.js 我这是jsp页面 <script type"text/jav…

idea方法注释模版设置

方法上面的注释模版&#xff1a; Template text: ** Description $desc$ $param$ $return$* Aauthor yimeng* date $DATE$ $TIME$ **/param&#xff1a; groovyScript("def result ;def params \"${_1}\".replaceAll([\\\\[|\\\\]|\\\\s], ).split(,).toLis…

机器学习笔记 - 3D数据的常见表示方式

一、简述 从单一角度而自动合成3D数据是人类视觉和大脑的基本功能,这对计算机视觉算法来说是比较难的。但随着LiDAR、RGB-D 相机(RealSense、Kinect)和3D扫描仪等3D传感器的普及和价格的降低,3D 采集技术的最新进展取得了巨大飞跃。与广泛使用的 2D 数据不同,3D 数据具有丰…

Opencv 极坐标变换

变换后图片 代码 // 以Center为极坐标原点&#xff0c;将RowFrom到RowTo的圆环&#xff0c;仅仅变换该范围内的点&#xff0c;忽略掉其他部分。 #include "polar_transeforme.hpp" #include <string>using namespace cv;void calculate_map(int rouFrom, int …

Matlab 在一个文件中调用另一个文件中的函数

文章目录 Part.I IntroductionPart.II 方法Chap.I A 文件中只有一个函数Chap.II A 文件中有多个函数 Part.I Introduction 本文介绍一下在脚本文件 B 中调用文件 A 中的函数的方法。 Part.II 方法 目的&#xff1a;在文件B.m调用A.m中的函数 默认两个文件在一个文件夹下&…