1、创建套接字:使用socket
函数创建一个套接字,例如int sockfd = socket(AF_INET, SOCK_STREAM, 0);
其中AF_INET
表示使用 IPv4 协议,SOCK_STREAM
表示使用 TCP 协议。
2、绑定地址:通过bind
函数将套接字与本地的一个 IP 地址和端口号绑定在一起。
定义一个sockaddr_in
结构体变量来存储地址信息。
设置其sin_family
为AF_INET
,sin_port
为指定的端口号(需要使用htons
函数将主机字节序转换为网络字节序),sin_addr.s_addr
可以设置为INADDR_ANY
表示绑定到所有可用接口。
调用bind(sockfd, (struct sockaddr *)&address, sizeof(address))
进行绑定。
3、监听连接:使用listen
函数将套接字设置为被动监听状态,等待客户端的连接请求,例如listen(sockfd, 3);
其中第二个参数是监听队列的最大长度,即最多可以有多少个连接请求在队列中等待。
4、接受连接:当有客户端请求连接时,使用accept
函数接受连接并创建一个新的套接字用于通信。
定义一个sockaddr_in
结构体变量来存储客户端的地址信息。
调用accept(sockfd, (struct sockaddr *)&client_address, &addrlen);
接受连接,返回值是一个新的套接字描述符,用于与该客户端进行通信。
5、处理请求:通过读写套接字来实现数据的收发,可以使用read
函数从套接字中读取数据,使用write
或send
函数向套接字发送数据。
6、示例代码:下面是一个简单的 C 语言实现的 TCP 服务器示例代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #define PORT 8080 int main() { int server_fd, new_socket; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1024] = {0}; char *hello = "Hello from server"; // 创建套接字 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置套接字选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt failed"); close(server_fd); exit(EXIT_FAILURE); } // 绑定地址 address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } // 监听连接 if (listen(server_fd, 3) < 0) { perror("listen failed"); close(server_fd); exit(EXIT_FAILURE); } printf("创建tcp服务器成功 "); // 接受连接并处理请求 while (1) { new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen); if (new_socket < 0) { perror("accept failed"); continue; } // 接收数据 int valread = read(new_socket, buffer, 1024); printf("Received: %s ", buffer); // 发送数据 send(new_socket, hello, strlen(hello), 0); printf("Response sent "); // 关闭套接字 close(new_socket); } return 0; }
以下是两个常见问题及解答:
问:为什么需要将端口号从主机字节序转换为网络字节序?
答:因为网络通信中使用的是网络字节序,而不同的计算机体系结构可能使用不同的字节序来存储数据,为了确保数据在网络传输过程中的正确性,需要将端口号等数据从主机字节序转换为网络字节序,在 C 语言中,可以使用htons
函数将主机字节序的短整型数据转换为网络字节序,使用htonl
函数将主机字节序的长整型数据转换为网络字节序。
问:如何同时处理多个客户端的连接请求?
答:可以通过多线程或多进程的方式来实现并发处理,当主线程接受到一个客户端的连接请求后,创建一个新的线程或进程来专门处理该客户端的请求,然后主线程继续接受其他客户端的连接请求,这样可以实现同时处理多个客户端的连接和数据传输,也可以使用 I/O 多路复用技术,如select
、poll
或epoll
等,来监控多个套接字的状态变化,提高服务器的性能和资源利用率。