2.1.1 网络io与io多路复用select/poll/epoll
- 搭建一个简单的服务端
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
int main()
{
// 1. create socket
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(2000);
// 2. bind socket
int flag = bind(listenFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
if (-1 == flag)
{
printf("bind error: %s\n", strerror(errno));
return -1;
}
// 3. listen
listen(listenFd, 10);
struct sockaddr_in clientAddr;
int len = sizeof(clientAddr);
int clientFd;
char buff[1024];
while(1)
{
memset(buff, 0, sizeof(buff));
// 4. accept client connection
clientFd = accept(listenFd,(struct sockaddr*)&clientAddr,&len);
// 5. recv data from client
recv(clientFd, buff, sizeof(buff), 0);
printf("recv data: %s\n", buff);
// 6. send data to client
memset(buff, 0, sizeof(buff));
sprintf(buff, "hello client, I have received your data");
send(clientFd, buff, strlen(buff), 0);
close(clientFd);
}
close(listenFd);
return 0;
}
上述代码可以循环的接收客户端连接,并且每一个客户端拥有一次和服务端收发数据的过程。但是有一个致命的确定,比如client1 client2 client3依此连接到服务端(先连接不发数据),此时会出现什么情况呢?如果我client3 client2 client1依此给server发消息,但只有当client1发出消息之后,client3和client2才会收到消息。
分析原因:client1 client2 client3依此连接到服务端时,程序阻塞在recv上,那么其他的client只有三次握手成功,并且阻塞在TCP队列上,并不能建立tcp连接。
- 改用多线程的方式
cpp
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <bits/pthreadtypes.h>
void handle_task(void* arg)
{
int clientFd = *(int*)arg;
char buff[1024];
memset(buff, 0, sizeof(buff));
while(1)
{
memset(buff, 0, sizeof(buff));
int count = recv(clientFd, buff, sizeof(buff), 0);
printf("recv data: %s\n", buff);
if(0 == count)
{
printf("client closed connection\n");
break;
}
memset(buff, 0, sizeof(buff));
sprintf(buff, "hello client, I have received your data");
send(clientFd, buff, strlen(buff), 0);
}
close(clientFd);
}
int main()
{
// 1. create socket
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(2000);
// 2. bind socket
int flag = bind(listenFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
if (-1 == flag)
{
printf("bind error: %s\n", strerror(errno));
return -1;
}
// 3. listen
listen(listenFd, 10);
struct sockaddr_in clientAddr;
int len = sizeof(clientAddr);
int clientFd;
char buff[1024];
static int count = 0;
#if 0
while(1)
{
memset(buff, 0, sizeof(buff));
// 4. accept client connection
clientFd = accept(listenFd,(struct sockaddr*)&clientAddr,&len);
printf("accept client connection, count: %d\n", ++count);
// 5. recv data from client
recv(clientFd, buff, sizeof(buff), 0);
printf("recv data: %s\n", buff);
// 6. send data to client
memset(buff, 0, sizeof(buff));
sprintf(buff, "hello client, I have received your data");
send(clientFd, buff, strlen(buff), 0);
close(clientFd);
}
#elif 1
// with multi thread
while(1)
{
// 4. accept client connection
clientFd = accept(listenFd,(struct sockaddr*)&clientAddr,&len);
pthread_t thid;
pthread_create(&thid, NULL, (void *)handle_task, (void *)&clientFd);
}
#endif
close(listenFd);
return 0;
}
上述代码在不断开tcp连接时候可以一直进行通信。
弊端:1. 如果tcp连接过多,超过10个之后,server将不能服务新的tcp连接。
- 如果tcp连接"占着茅坑不拉屎",那么是对server资源的一种浪费。