• linux C语言 socket的server、client 实现


    讲解:

    Linux中,使用socket与另一端建立连接通常涉及到以下步骤:

    1. 创建Socket:首先,你需要创建一个套接字(socket)。你可以使用socket()系统调用来创建套接字。通常,你需要指定套接字的类型(例如,SOCK_STREAM表示TCP套接字)和地址族(例如,AF_INET表示IPv4地址族)。

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    2. 设置目标地址信息:你需要指定要连接的远程服务器的地址信息,包括IP地址和端口号。通常,你会使用struct sockaddr_in来表示这些信息。

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // 服务器的端口号
    inet_pton(AF_INET, "服务器IP地址", &(server_addr.sin_addr));

    3. 使用connect()函数建立连接:接下来,你可以使用connect()系统调用来与远程服务器建立连接。

    int connection_status = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (connection_status == -1) {
        perror("connect");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    4. 发送和接收数据:一旦连接建立成功,你可以使用send()recv()等函数来发送和接收数据。

    5. 关闭套接字:当通信完成后,不要忘记关闭套接字,释放资源。

    close(sockfd);

    以上是使用C语言的套接字编程的基本步骤。请注意,错误处理非常重要,以确保你的程序在出现问题时能够 graceful 地处理它们。此外,还可以考虑使用非阻塞套接字或多线程/多进程来处理连接,以提高性能和可伸缩性。具体实现方式可能会因编程语言和库的不同而有所不同。

    实例一

    如何编写两个进程,一个进程作为socket的服务端,一个进程作为socket的客户端,服务端监听客户端:

    要编写一个简单的Socket通信程序,其中一个进程作为服务器端监听客户端,另一个进程作为客户端连接到服务器,可以使用C语言编写,以下是一个基本的示例:

    1. 服务端程序(server.c):
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        // 创建套接字
        int server_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (server_sock == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        // 设置服务器地址信息
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(8080);
        server_addr.sin_addr.s_addr = INADDR_ANY;
    
        // 绑定套接字到服务器地址
        if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
        // 监听连接请求
        if (listen(server_sock, 5) == -1) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
    
        printf("Server listening on port 8080...\n");
    
        // 接受客户端连接请求
        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_sock == -1) {
            perror("accept");
            exit(EXIT_FAILURE);
        }
    
        printf("Client connected.\n");
    
        // 在这里可以进行与客户端的数据交互
    
        // 关闭套接字
        close(client_sock);
        close(server_sock);
    
        return 0;
    }
    2. 客户端程序(client.c):
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        // 创建套接字
        int client_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (client_sock == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        // 设置服务器地址信息
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(8080);
        server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        // 连接到服务器
        if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
            perror("connect");
            exit(EXIT_FAILURE);
        }
    
        printf("Connected to server.\n");
    
        // 在这里可以进行与服务器的数据交互
    
        // 关闭套接字
        close(client_sock);
    
        return 0;
    }

    你可以将上述代码分别保存为 server.c 和 client.c,并分别编译成可执行文件,然后在两个不同的终端窗口中运行它们。服务器将在端口8080上监听连接,而客户端将连接到服务器。在服务器和客户端之间进行数据交互的部分可以根据你的需求进行扩展。

    编译

    要编译生成可执行文件,你可以使用 C 编译器(如 GCC)来编译你的 C 代码。以下是编译和生成可执行文件的步骤:

    1. 打开终端窗口,并导航到包含你的 C 源代码文件的目录。
    2. 使用编译器编译你的源代码文件。对于上面提供的示例代码,你可以使用以下命令编译:
      对于服务端程序 server.c:
    gcc -o server server.c


    对于客户端程序 client.c:
    这些命令中的 -o 选项用于指定生成的可执行文件的名称。在上面的示例中,我们将生成的可执行文件分别命名为 serverclient

    1. 如果编译没有错误,你会在同一目录下生成可执行文件 serverclient
    2. 要运行服务器程序,你可以在终端中执行以下命令:
    ./server


    要运行客户端程序,你可以执行以下命令:
    这将启动服务器和客户端,并开始它们之间的通信。

    请确保在编译之前安装了合适的编译器,例如 GCC。如果你使用的是不同的编程语言或开发环境,编译步骤可能会有所不同。但通常情况下,使用编译器将源代码编译成可执行文件是一个常见的编译过程。

    实例二(实际应用):

    在实际应用中,不可能像实例一中,server只接收一次,client只向server发送一次。如何做到,server始终存在监听端口,当有client连接时,接收包并处理

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include   // 包含fcntl头文件
    
    int main() {
        // 创建套接字
        int server_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (server_sock == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        // 设置服务器地址信息
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(8080);
        server_addr.sin_addr.s_addr = INADDR_ANY;
    
        // 绑定套接字到服务器地址
        if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
        // 监听连接请求
        if (listen(server_sock, 5) == -1) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
    
        printf("Server listening on port 8080...\n");
    
        // 接受客户端连接请求
        while (1) {
            struct sockaddr_in client_addr;
            socklen_t client_addr_len = sizeof(client_addr);
            int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
            if (client_sock == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
    
            printf("Client connected.\n");
    
            // 发送数据给客户端
            char message[] = "Hello, client!";
            if (send(client_sock, message, sizeof(message), 0) == -1) {
                perror("send");
            }
    
            while (1) {
                // 接收客户端的数据,recv函数会阻塞等待数据到达
                char buffer[1024];
                int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
                if (bytes_received == -1) {
                    perror("recv");
                    break; // 出错时退出循环
                } else if (bytes_received == 0) {
                    // 客户端关闭连接,退出循环
                    printf("Client disconnected.\n");
                    break;
                } else {
                    buffer[bytes_received] = '\0';
                    printf("Received from client: %s\n", buffer);
                }
            }
            // 关闭套接字
            close(client_sock);
        }
        close(server_sock);
    
        return 0;
    }
    

    需要注意: server中的recv是阻塞式的情况基于client_sock未关闭且资源未释放才可以,否则recv始终返回0.

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        // 创建套接字
        int client_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (client_sock == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        // 设置服务器地址信息
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(8080);
        server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        // 连接到服务器
        if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
            perror("connect");
            exit(EXIT_FAILURE);
        }
    
        printf("Connected to server.\n");
    
        // 接收服务器的数据
        char buffer[1024];
        int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
        if (bytes_received == -1) {
            perror("recv");
        } else {
            buffer[bytes_received] = '\0';
            printf("Received from server: %s\n", buffer);
        }
        sleep(10);
        // 发送数据给服务器
        while (1) {
            char message[] = "Hello, server!";
            if (send(client_sock, message, sizeof(message), 0) == -1) {
                perror("send");
            }
            sleep(10);
        }
        
        // 关闭套接字, 只要关闭client,server端的recv就不会阻塞了,返回0!!
        close(client_sock);
    
        return 0;
    }
    

    实验效果:

    如果对你有帮助,请点赞收藏,谢谢~这是我更新的动力~
    如果有任何问题欢迎在评论区交流~我们一同成长~

  • 相关阅读:
    【大数据处理技术】实验12(新建文件夹)
    P7557 [USACO21OPEN] Acowdemia S
    【你不知道的javascript上】this指针,函数的call、apply、bind,new 操作符到底发生了什么
    【python】读取.dat格式文件
    FFmpeg入门详解之24:短视频技术原理
    如何修改论文,能够避开查重?
    【数据结构】串的定义;存储结构;基本操作的实现
    CDH5.12.0-HiveServer2-java.net.SocketTimeoutException: Read timed out
    Flink---5、聚合算子、用户自定义函数、物理分区算子、分流、合流
    学习Java框架的重要性及步骤
  • 原文地址:https://blog.csdn.net/nianzhu2937/article/details/132827855