一、什么是网络IO?
就是客户端和服务端之间的进行通信的通道(fd)。
二、网络IO通信步骤
1、建立套接字
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //绑定0.0.0.0,Linux上有三张网卡,eth0:桥接,eth1:NAT,lo:回环,
// 0.0.0.0代表任意一个收到数据都行,也可以绑定固定网卡上的ip地址
servaddr.sin_port = htons(2000); // 0~1023:系统使用,1024以后用户可以使用
if (-1 == bind(socketfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)))
{
cout << "bind failed:" << strerror(errno) << endl;
}
2、实现监听
listen(socketfd, 10); //监听
struct sockaddr_in clientaddr; //创建一个客户端用于接受数据
socklen_t len = sizeof(clientaddr);
cout<<"current socketfd:"<<socketfd<<endl;
int clientfd = accept(socketfd, (struct sockaddr*)&clientaddr, &len);
cout<<"clientfd is:"<<clientfd<<endl;
netstat -anop | grep 2000 #新建一个窗口,输入该命令查看绑定的2000端口状态, 22查ssh,3306查mysql,6309查redis
如果再开一个窗口执行监听,那么会报错
3、接受数据 fd与tcp连接,io通道建立成功
会一直阻塞等待数据的到来
char buffer[1024] = {0};
int count = recv(clientfd, buffer, 1024, 0);
cout<<"buffer:"<<buffer<<endl;
4、返回信息
count = send(clientfd, buffer,count, 0);
5、由于代码中只存在两个fd,socketfd和clientfd
当存在一个客户端连接服务端时,可以正常收发消息;但当出现多个客户端连接服务端时,只有一个客户端能发信息,其余
客户端不能发送信息
客户端发送消息结果:
小结:
虽然3个客户端都连接服务端成功,但最终通信成功的只有一个,acept就调用成功一次就结束了,自然就接收不到其余两个客户端的数据
优化代码:
while (true)
{
cout<<"current socketfd:"<<socketfd<<endl;
int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);
cout<<"clientfd is:"<<clientfd<<endl;
char buffer[1024] = {0};
int count = recv(clientfd, buffer, 1024, 0);
cout << "buffer:" << buffer << endl;
//返回信息
count = send(clientfd, buffer, count, 0);
}
小结:
虽然都通信成功,但只能发送一次消息,无法发送第二次消息,在acept这进行了阻塞,而且必须根据连接顺序来处理信息,
比如连接顺序是1号,2号,3号客户端,3号连发3次消息,2号连发2次消息,此时服务端仍然没反应,在等待1号发消息,
1号发消息,然后服务端依次处理,将2号的2次消息,3号的3次消息全部发出,然后阻塞,需要有新的连接,然后才能处理消息。
存在两个问题:
1、无法实时响应。
2、一次性连接处理信息问题
三、多个IO如何接收数据
1、解决实时性问题和一次性连接处理问题
当多个客户端连接服务器时,accept会进行阻塞,等待第一个用户数据接收完之后,才处理第二个用户的请求…
解决方案:
采用多线程来解决。
void *ClientPthread(void *arg)
{
int clientfd = *(int *)arg;
while(true){
cout<<"clientfd is:"<<clientfd<<endl;
char buffer[1024] = {0};
int count = recv(clientfd, buffer, 1024, 0);
cout << "buffer:" << buffer << endl;
//返回信息
count = send(clientfd, buffer, count, 0);
}
}
while (true)
{
cout<<"current socketfd:"<<socketfd<<endl;
int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);
pthread_t p_id;
pthread_create(&p_id, NULL, ClientPthread, &clientfd);
}
2、讨论fd的值
无论什么时候连接,或者多个客户端连接,发现socketfd的值永远是3,而接收客户端返回的fd却是在3的基础上+1
那么fd,0,1,2去哪了
ls /dev/fd
原来这三个值分别是代表标准输入,输出以及错误
那允许多少客户端连接呢,fd是个整数,肯定也存在一定的范围
ulimit -a
当前最多只支持到这么多
3、fd支持有限,那客户端断开连接后,原来的fd还能用吗?
答案是肯定的,系统有自己的回收策略
int count = recv(clientfd, buffer, 1024, 0);
if(count == 0){ //断开
close(clientfd); //必须要有close,不然系统不会回收fd资源
break;
}