蓝桉云顶

Good Luck To You!

如何在Linux中选择TCP协议进行网络通信?

在 Linux 中,使用 ss 命令可以查看和管理 TCP 连接。ss -t 列出所有 TCP 连接。

在Linux操作系统中,TCP(传输控制协议)是网络通信的核心组件之一,它提供了一种可靠的、面向连接的数据传输方式,确保数据包按顺序和无错误地到达目标地址,本文将深入探讨如何在Linux环境下使用select系统调用来监控多个TCP套接字(sockets),以及这种技术如何帮助开发者实现高效的网络编程。

理解TCP与Socket

TCP是一种面向连接的协议,意味着在数据传输之前,通信双方需要先建立一个连接,这个连接通过套接字(socket)来实现,套接字是一个通信端点,可以看作是网络通信中的一个“电话”,在Linux中,套接字可以通过文件描述符来引用,就像普通文件一样进行读写操作。

`select`系统调用

select是Linux提供的一个系统调用,用于监视文件描述符集合的变化情况,特别是检查一个或多个套接字是否有数据可读、可写或有异常条件发生,这对于编写能够同时处理多个网络连接的服务器程序尤为重要,因为它允许程序在不阻塞的情况下等待多个事件的发生。

select函数原型

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds: 要检查的文件描述符数量。

readfds: 指向要监视可读性的文件描述符集合。

writefds: 指向要监视可写性的文件描述符集合。

exceptfds: 指向要监视异常情况的文件描述符集合。

timeout: 指定等待的最长时间,如果为NULL则无限期等待。

返回值:

成功时返回准备好的文件描述符数量。

失败时返回-1,并设置errno

如果超时则返回0。

使用`select`监控TCP套接字

假设我们有一个多客户端的TCP服务器,想要同时处理来自不同客户端的连接请求和数据发送,以下是一个简单的示例代码片段,展示了如何使用select来实现这一目标。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define MAXLINE 1024
int main() {
    int listener, new_socket, max_sd, activity, valread, sd;
    int client_socket[30], max_clients = 30; // 最大客户端数
    struct sockaddr_in address;
    char buffer[MAXLINE];
    fd_set readfds;
    int opt = 1;
    int addrlen = sizeof(address);
    // 创建监听套接字
    if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    // 绑定地址和端口
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    if (bind(listener, (struct sockaddr *)&address, sizeof(address))<0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    // 开始监听
    if (listen(listener, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    printf("Listening on port %d
", PORT);
    while (1) {
        FD_ZERO(&readfds);
        FD_SET(listener, &readfds);
        max_sd = listener;
        // 添加现有套接字到集合中
        for (int i = 0; i < max_clients; i++) {
            sd = client_socket[i];
            if (sd > 0) FD_SET(sd, &readfds);
            if (sd > max_sd) max_sd = sd;
        }
        // 等待事件发生
        activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);
        if (activity < 0 && errno != EINTR) {
            printf("select error");
        }
        // 如果监听套接字上有事件发生,接受新的连接
        if (FD_ISSET(listener, &readfds)) {
            if ((new_socket = accept(listener, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            printf("New connection, socket fd is %d
", new_socket);
            // 将新套接字添加到数组中
            for (int i = 0; i < max_clients; i++) {
                if (client_socket[i] == 0) {
                    client_socket[i] = new_socket;
                    printf("Adding to list of sockets as %d
", i);
                    break;
                }
            }
        }
        // 处理已连接的套接字上的事件
        for (int i = 0; i < max_clients; i++) {
            sd = client_socket[i];
            if (FD_ISSET(sd, &readfds)) {
                // 读取数据并响应
                valread = read(sd, buffer, 1024);
                buffer[valread] = '\0';
                printf("Received: %s
", buffer);
                send(sd, buffer, strlen(buffer), 0);
            }
        }
    }
    return 0;
}

表格:select与其他I/O多路复用技术的对比

特性selectpollepollkqueue/dev/pollio_uring
系统兼容性 广泛支持 广泛支持 Linux特有 BSD特有 Solaris特有 Linux 5.1+
性能 较低(线性扫描) 中等 高(基于事件驱动) 中等 极高
使用复杂度 简单 简单 中等 中等 中等 复杂
适用场景 少量文件描述符 少量至中等量级文件描述符 大量文件描述符 大量文件描述符 大量文件描述符 高性能需求场景
资源占用 较高(每次调用都需要复制fd_set) 中等 低(动态管理) 极低

FAQs

Q1:select系统调用为什么被称为“线性扫描”?

A1:select系统调用之所以被称为“线性扫描”,是因为它会遍历所有传入的文件描述符集(readfds,writefds,exceptfds),即使这些集合中的大部分文件描述符都没有就绪事件,这种遍历方式导致其时间复杂度为O(n),其中n是监视的文件描述符数量,当文件描述符数量很大时,这会成为性能瓶颈。

Q2: 何时使用select而非其他更高效的I/O多路复用技术?

A2:select虽然在某些方面不如poll,epoll,kqueue等现代I/O多路复用技术高效,但它仍然有其适用场景:

跨平台需求:如果你需要编写跨多种操作系统的代码,而不仅仅是Linux,select是一个很好的选择,因为它在大多数Unix-like系统上都有实现。

简单性:对于简单的应用程序,或者学习网络编程的基础概念时,select提供了一个易于理解和使用的接口。

资源限制较小的环境:在文件描述符数量不是非常多,且对性能要求不是特别高的场合,select足够应付,且实现起来更直接。

各位小伙伴们,我刚刚为大家分享了有关“select linux tcp”的知识,希望对你们有所帮助。如果您还有其他相关问题需要解决,欢迎随时提出哦!

  •  纯美
     发布于 2024-02-18 14:01:45  回复该评论
  • linux用什么写python「怎么用linux写python」这本书解答了如何在Linux环境下编写Python程序的问题,为Linux初学者提供了实用的指导。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«    2024年11月    »
123
45678910
11121314151617
18192021222324
252627282930
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接