【C/C++】网络编程入门
本文基于一请求一线程框架,实现一个服务器与多个客户端连接,彼此互不影响
函数介绍
- socket:创建文件描述符 fd
- bind:服务端绑定端口,绑定失败返回 -1
- listen:服务端开始监听
- accept:服务端接受客户端连接,未接受到则阻塞,返回文件描述符 fd
- recv:服务端接受客户端发送数据,保存到 buffer 缓冲区,未接受到则阻塞,返回实际接收到数据的字节数,客户端断开返回0
- send:服务端从buffer中取出数据发送到客户端
- close:关闭文件描述符
过程描述
- 服务器启动,创建 sockfd,阻塞在 accept 位置,等待客户端连接
- 客户端连接服务器,创建 clientfd,pthread_create 创建线程,阻塞在 recv 位置
- 客户端主动发送消息,该线程完成一个 while 循环,继续阻塞在 recv 位置
- 客户端退出连接,此时 recv 返回 0,关闭 clientfd,跳出循环,线程结束
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>
void* client_thread(void* arg) {
int clientfd = *(int *)arg;
while (1) {
char buffer[1024] = {0};
int count = recv(clientfd, buffer, sizeof(buffer), 0);
if (count == 0) {
close(clientfd);
printf("client %d disconnected\n", clientfd);
break;
}
printf("RECV %s\n", buffer);
send(clientfd, buffer, count, 0);
printf("SEND %d\n", count);
}
}
int main(void) {
//服务端
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(2000);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof (struct sockaddr)) == -1) {
printf("bind error %d", sockfd);
return -1;
}
listen(sockfd, 10);
printf("listening... %d\n", sockfd);
//客户端
struct sockaddr_in clientaddr;
socklen_t len = sizeof (struct sockaddr);
while (1) {
printf("accepting...\n");
int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
printf("client %d accepted\n", clientfd);
pthread_t tid;
pthread_create(&tid, NULL, client_thread, &clientfd);
}
return 0;
}
常见问题
- 出现 bind error,通过 netstat -an | grep 2000,发现连接处于 TIME_WAIT,此时上一次连接强制断开,稍等片刻重连即可
总结
- 服务端和客户端连接需要分别创建一个 sockfd 和 clientfd,clientfd 的生命周期与该客户端连接的生命周期一致
- 每有一个客户端就会新建一个 clientfd,该方法相当于一个线程管理一个 clientfd,在后续客户端数量很多的时候性能低下
- 后续优化可以从单线程管理多个 clientfd,优化 fd 的管理模式上进行