0voice-2.1.1-网络io
bind 和 listen
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET; //`AF_INET` 代表 `IPv4` , `AF_INET6` 代表 `IPv6`。
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //绑定网卡地址,0.0.0.0。
servaddr.sin_port = htons(2000); //0 - 1023端口是系统默认的,从 1024 开始使用。
if (-1 == bind(sockfd , (struct sockaddr*)&servaddr, sizeof(struct sockaddr))) {
printf("bind failed: %s\n",strerror(errno));
}
listen(sockfd , 10); //设置待处理连接队列的长度 (backlog = 10)
printf("listen finshed\n");
- 总的来说就是,监听本地网络所有可用网络接口上的
2000端口 。 INADDR_ANY是通配地址0.0.0.0, 代表监听所有地址,当然这里的参数也可以填写本地的其他IP地址 。- 注意不能反复绑定同一个端口 。
sockfd代表的是服务器的公共入口,它在等待任何潜在的客户端。它不属于任何一个特定的“用户”或“会话”,其职责职责是产生那些最终会与userinfo建立映射关系的fd(即clientfd)。后文也会说明一个sockfd能对应多个clientfd。- 监测端口的命令
netstat -anop | grep 2000。
accept 和 recv
- 版本 \(1\)
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
printf("accept\n");
int clientfd = accept(sockfd , (struct sockaddr*)&clientaddr , &len);
printf("accept finished\n");
char buffer[1024] = {0};
int count = recv(clientfd , buffer , 1024 , 0);
printf("RECV %s\n", buffer);
count = send(clientfd , buffer , count , 0);
printf("SEND: %d\n", count);
- 注意
accept和accept finished是不同的,accept()是会进行阻塞的。 accept产生clientfd。- 这个版本的缺陷就是只能建立一次
accept,如果有第二个客户端请求服务,则无法accept finished。
- 版本 \(2\)
while (1) {
printf("accept\n");
int clientfd = accept(sockfd , (struct sockaddr*)&clientaddr , &len);
printf("accept finished\n");
char buffer[1024] = {0};
int count = recv(clientfd , buffer , 1024 , 0);
printf("RECV %s\n", buffer);
count = send(clientfd , buffer , count , 0);
printf("SEND: %d\n", count);
}
- 缺陷:单线程的模式,当有 \(1\) , \(2\) , \(3\)客户顺序请求
accept的时候,只有当 \(1\) 客户 \(recv\) 完毕的时候,\(2\) , \(3\)客户才能accept finished, 在之前所有的accept和recv请求只能被阻塞。
- 版本 \(3\)
void *client_thread(void *arg) {
int clientfd = *(int *)arg; // 从传入的参数中获取 clientfd
while (1) { //防止只能接受一条
char buffer[1024] = {0};
int count = recv(clientfd , buffer , 1024 , 0);
printf("RECV %s\n", buffer);
if (count == 0) { //discount
printf("client disconnect: %d\n",clientfd);
close(clientfd);
break;
}
count = send(clientfd , buffer , count , 0);
printf("SEND: %d\n", count);
}
}
while (1) {
printf("accept\n");
int clientfd = accept(sockfd , (struct sockaddr*)&clientaddr , &len);
printf("accept finished\n");
pthread_t thid;
// 创建一个新的线程
// 参数1: 存储新线程的ID
// 参数2: 线程属性 (NULL表示使用默认属性)
// 参数3: 新线程将执行的函数 (这里是 client_thread)
// 参数4: 传递给 client_thread 函数的参数 (这里是新接受的 clientfd 的地址)
pthread_create(&thid , NULL , client_thread , &clientfd);
}
- 为每一个
clientfd创造一个新的线程,一个线程服务于一个客户端。
补充
-
当
recv回馈是0的时候,代表断开连接。 -
\(fd\) 的值有依次增加
3,4,5,6( 无论是sockfd还是clientfd)0,1,2在哪里? 系统已经封装好了ls /dev/stdin -l,ls /dev/stdout -l和ls /dev/stderr -lulimit -a: 看每一个进程io的数量,进程的fd是有限的。close(fd)回收和分配的机制,有回收的时间,分配会分配最小的那个未被使用的fd
select
-
之前的模式:\(1\) 请求 , \(1\) 线程
- 好处:代码逻辑简单
- 缺点:不利于并发,1 \(k\) 并发量左右
-
select提供文件集合
fd_set,集合的大小

浙公网安备 33010602011771号