当前位置: 首页 > article >正文

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;
}

http://www.kler.cn/a/150275.html

相关文章:

  • PyQt5的安装与简介
  • python 使用进程池并发执行 SQL 语句
  • GitHub | 发布到GitHub仓库并联文件夹的方式
  • pnpm install安装element-plus的版本跟package.json指定的版本不一样
  • HTML学习笔记十
  • 主格、宾格、形容词性物主代词和名词性物主代词
  • 《尚品甄选》:后台系统——分类品牌和规格管理(debug一遍)
  • Docker容器网络模式
  • PHP如何实现邮箱验证
  • Android控件全解手册 - 多语言切换完美解决方案(兼容7.0以上版本)
  • 找不到 sun.misc.BASE64Decoder ,sun.misc.BASE64Encoder 类
  • ESP32-Web-Server 实战编程- 使用 AJAX 自动更新网页内容
  • pytest分布式执行(pytest-xdist)
  • rabbitmq-server-3.11.10.exe
  • 基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(三)
  • Linux CentOS7 fdisk
  • 面试题:Spring 中获取 Bean 的方式有哪些?
  • 如何生成唯一ID:探讨常用方法与技术应用
  • 运维知识点-openResty
  • 代码随想录-刷题第七天
  • element table滚动到底部加载数据(vue3)
  • C语言进阶指南(11)(指针数组与二维数组)
  • 拉普拉斯变换
  • 字母大小写转换
  • PHP微信UI在线聊天系统源码 客服私有即时通讯系统 附安装教程
  • kafka C++实现生产者