凌峰创科服务平台

linux c tcp服务器

Linux C TCP服务器开发是网络编程中的核心内容,它通过套接字(Socket)实现基于TCP协议的双向通信,TCP是一种面向连接的可靠传输协议,具有数据顺序传输、错误重传和流量控制等特点,适用于需要高可靠性的场景,如文件传输、远程登录等,下面将详细介绍Linux C TCP服务器的实现原理、关键步骤及代码示例,并辅以表格说明关键函数的功能。

linux c tcp服务器-图1
(图片来源网络,侵删)

TCP服务器的基本工作流程

TCP服务器的工作流程遵循严格的步骤,主要包括创建套接字、绑定地址端口、监听连接、接受连接、数据传输和关闭连接等阶段,每个阶段都有对应的系统调用,开发者需要正确调用这些函数并处理可能的错误。

创建套接字

套接字是网络通信的端点,通过socket()函数创建,该函数需要指定地址族(如AF_INET表示IPv4)、套接字类型(SOCK_STREAM表示TCP)和协议(通常为0,由系统自动选择)。

int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
    perror("socket creation failed");
    exit(EXIT_FAILURE);
}

绑定地址和端口

服务器需要将套接字绑定到特定的IP地址和端口,以便客户端能够找到它,使用bind()函数,需先填充sockaddr_in结构体,指定地址族(AF_INET)、端口号(htons将端口号转换为网络字节序)和IP地址(INADDR_ANY表示监听所有接口):

struct sockaddr_in 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_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}

监听连接

listen()函数将套接字设置为被动模式,等待客户端连接请求,第二个参数表示最大连接队列长度:

linux c tcp服务器-图2
(图片来源网络,侵删)
if (listen(server_socket, 5) < 0) {
    perror("listen failed");
    exit(EXIT_FAILURE);
}

接受连接

accept()函数从连接队列中取出一个客户端连接,返回一个新的套接字用于与该客户端通信,原套接字继续监听其他连接:

struct sockaddr_in client_addr;
int client_socket;
socklen_t client_addr_len = sizeof(client_addr);
client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
if (client_socket < 0) {
    perror("accept failed");
    exit(EXIT_FAILURE);
}
printf("Client connected: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

数据传输

使用recv()send()函数与客户端进行数据收发。recv()从客户端读取数据,send()向客户端发送数据,需要注意处理部分读取的情况:

char buffer[1024] = {0};
int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received < 0) {
    perror("recv failed");
} else {
    printf("Received: %s\n", buffer);
    send(client_socket, "Message received", 16, 0);
}

关闭连接

数据传输完成后,关闭客户端套接字和服务器套接字:

close(client_socket);
close(server_socket);

关键函数及参数说明

以下是TCP服务器开发中常用函数的详细说明:

linux c tcp服务器-图3
(图片来源网络,侵删)
函数名 功能描述 关键参数 返回值
socket() 创建套接字 domain:地址族(AF_INET);type:套接字类型(SOCK_STREAM);protocol:协议 成功返回套接字描述符,失败返回-1
bind() 绑定套接字到指定地址和端口 sockfd:套接字描述符;addr:地址结构体指针;addrlen:地址结构体长度 成功返回0,失败返回-1
listen() 设置套接字为监听状态 sockfd:套接字描述符;backlog:最大连接队列长度 成功返回0,失败返回-1
accept() 接受客户端连接 sockfd:监听套接字;addr:客户端地址结构体指针;addrlen:地址长度指针 成功返回客户端套接字,失败返回-1
recv() 从套接字接收数据 sockfd:套接字描述符;buf:接收缓冲区指针;len:缓冲区长度;flags:标志位 成功返回接收字节数,失败返回-1
send() 通过套接字发送数据 sockfd:套接字描述符;buf:发送缓冲区指针;len:数据长度;flags:标志位 成功返回发送字节数,失败返回-1
close() 关闭套接字 fd:套接字描述符 成功返回0,失败返回-1

完整代码示例

以下是一个简单的TCP服务器实现,支持多客户端连接(使用多线程):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <pthread.h>
void* handle_client(void* arg) {
    int client_socket = *(int*)arg;
    free(arg);
    char buffer[1024] = {0};
    int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
    if (bytes_received < 0) {
        perror("recv failed");
    } else {
        printf("Client message: %s\n", buffer);
        send(client_socket, "Server response", 15, 0);
    }
    close(client_socket);
    return NULL;
}
int main() {
    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }
    struct sockaddr_in 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_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    if (listen(server_socket, 5) < 0) {
        perror("listen failed");
        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_socket = malloc(sizeof(int));
        *client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
        if (*client_socket < 0) {
            perror("accept failed");
            free(client_socket);
            continue;
        }
        pthread_t thread;
        if (pthread_create(&thread, NULL, handle_client, client_socket) != 0) {
            perror("thread creation failed");
            close(*client_socket);
            free(client_socket);
        }
        pthread_detach(thread);
    }
    close(server_socket);
    return 0;
}

常见问题与优化

  1. 端口占用问题:如果端口已被占用,bind()会失败,可通过netstat -tulpn检查端口使用情况,或修改端口号。
  2. 多客户端处理:单线程服务器无法同时处理多个客户端,可通过多线程、多进程或I/O多路复用(如selectepoll)优化性能。

FAQs

问题1:TCP服务器如何处理大量并发连接?
解答:可以通过以下方式优化:

  1. 多线程/多进程:为每个客户端创建线程或进程,但资源消耗较大。
  2. I/O多路复用:使用selectpollepoll(Linux特有)实现单线程处理多个连接,epoll在大量连接时性能更优。
  3. 线程池:预创建一组线程,避免频繁创建和销毁线程的开销。

问题2:TCP服务器如何避免数据粘包问题?
解答:TCP是流式协议,可能出现粘包(多个数据包合并)或拆包(一个数据包被拆分),解决方法包括:

  1. 固定长度协议:每个数据包长度固定,接收方按长度读取。
  2. 特殊分隔符:在数据包末尾添加特定分隔符(如\n),接收方按分隔符分割。
  3. 长度字段:在每个数据包头部添加长度字段,接收方先读取长度,再读取对应长度的数据。
分享:
扫描分享到社交APP
上一篇
下一篇