linux C语言 socket的server、client 实现

9 篇文章 1 订阅
5 篇文章 0 订阅

讲解:

在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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

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 <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>  // 包含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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

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

实验效果:

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

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 Linux C 语言 socket 服务端循环监听端口的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8888 // 监听端口号 int main() { int sockfd, connfd; struct sockaddr_in servaddr, cliaddr; socklen_t len = sizeof(cliaddr); char buffer[1024]; // 创建 socket 文件描述符 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(EXIT_FAILURE); } // 初始化服务器 socket 地址 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; // 协议族 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY 表示任意地址 servaddr.sin_port = htons(PORT); // 端口号 // 绑定 socket 文件描述符和服务器 socket 地址 if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { perror("bind"); exit(EXIT_FAILURE); } // 开始监听连接请求 if (listen(sockfd, 5) == -1) { perror("listen"); exit(EXIT_FAILURE); } printf("Server listening on port %d...\n", PORT); while (1) { // 接受客户端连接请求 connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len); if (connfd == -1) { perror("accept"); continue; } printf("Accepted new connection from %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); // 读取客户端发来的数据 read(connfd, buffer, sizeof(buffer)); printf("Received message: %s\n", buffer); // 回复客户端 write(connfd, "Hello, client!", strlen("Hello, client!")); // 关闭客户端连接 close(connfd); } // 关闭服务器 socket close(sockfd); return 0; } ``` 这段代码创建了一个 TCP socket 文件描述符,通过 `bind()` 函数将其绑定到指定的端口号,然后通过 `listen()` 函数开始监听连接请求。在 `while` 循环中,通过 `accept()` 函数等待客户端连接请求,并在连接建立后读取客户端发送的数据并回复。循环不断接受新的连接请求,直到服务器关闭。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值