在第一篇文章中,我们介绍了如何创建套接字以及如何将其绑定到特定的地址和端口。本文是第二篇,将继续深入探讨网络编程,重点介绍如何监听连接和接受连接。
一、监听连接
1. listen()函数
listen()函数用于将套接字转换为监听套接字,使其能够接受来自客户端的连接请求。
int listen(int sockfd, int backlog);sockfd:由socket()函数创建并绑定到地址的套接字文件描述符。backlog:指定队列的最大长度,用于存放等待处理的连接请求。返回值:成功时返回0,失败时返回-1。
示例
// 监听连接,最大等待连接数为5if (listen(sock, 5) == -1) { perror("listen failed"); exit(EXIT_FAILURE);}二、接受连接
1. accept()函数
accept()函数用于从已完成连接队列中接受一个连接请求,并返回一个新的套接字文件描述符,用于与客户端通信。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);sockfd:监听套接字的文件描述符。addr:指向sockaddr结构的指针,用于存储客户端的地址信息。addrlen:指向sockaddr结构长度的指针。返回值:成功时返回新的连接套接字文件描述符,失败时返回-1。
2. sockaddr结构
与绑定地址时使用的sockaddr结构相同,accept()函数也使用这个结构来获取客户端的地址信息。
示例
struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);// 接受客户端连接int client_sock = accept(sock, (struct sockaddr *)&client_addr, &client_addr_len);if (client_sock == -1) { perror("accept failed"); exit(EXIT_FAILURE);}// 打印客户端IP地址和端口号printf("Client connected: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));三、并发处理连接
在实际应用中,服务器通常需要处理多个客户端连接。为了实现这一点,服务器程序通常会为每个接受的连接创建一个新的线程或进程,以便并行处理。
示例:创建线程处理连接
void *handle_connection(void *arg) { int client_sock = *(int *)arg; // 处理客户端连接 // ... close(client_sock); // 处理完毕后关闭连接 return NULL;}// 接受客户端连接int client_sock = accept(sock, (struct sockaddr *)&client_addr, &client_addr_len);if (client_sock == -1) { perror("accept failed"); exit(EXIT_FAILURE);}// 创建线程处理连接pthread_t thread_id;if (pthread_create(&thread_id, NULL, handle_connection, &client_sock) != 0) { perror("pthread_create failed"); close(client_sock);}四、总结
本篇文章介绍了如何使用listen()函数将套接字设置为监听状态,以及如何使用accept()函数接受客户端的连接请求。这两个函数是服务器端网络编程的关键步骤,它们使得服务器能够被动地等待并接受来自客户端的连接。在下一篇文章中,我们将介绍如何使用套接字进行数据的发送和接收。