版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/89914110
前面通过信号捕捉也没有完美的解决客服端阻塞的问题, 那么本节我们通过将客服端的IO操作分离的办法来解决这个困惑我们很久的bug吧.
IO分离
第一次听到 IO分离 肯定会感觉很陌生, 不急, 下面我会解释的.
IO分离实际是将 read
逻辑操作(也就是读操作) 和 write
逻辑操作(也就是写操作) 的实现分离开. 分离技术有多种, 本节也仅仅介绍一种简单.
疑惑
在SIGPIPE信号中我们分析到阻塞的原因是出现在 read
函数, 那么如果我们能在 write
返回为0直接退出客服端程序不就可以避免阻塞了吗?
本节就将实现客服端的IO分离. 但既然可以在 write
返回0直接调用 exit
退出程序为什么还要做IO分离呢? 将IO分离有优势.
- 传统的回声客服端 : 将数据发送给客服端之后下一步无条件的等待对端将数据发送过来再执行下一批的传送.
- IO分离 : 客服端发送数据不必考虑接收数据的情况, 因此可以连续的发送数据, 提高传输效率. 这在网速慢的情况下尤为明显.
IO分离实现
IO分离的实现其实可以直接采用fork
就行了. 一个进程只负责向套接字中写数据, 一个进程负责从套接字中读数据.
这里将修改过后服务端的代码粘贴出来.
void Write(int sockfd){ // 写操作
int len;
char buf[1024];
while(1){
len = read(STDIN_FILENO, buf, sizeof(buf));
if(len == 0)
break;
write(sockfd, buf, len);
}
close(sockfd);
}
void Read(int sockfd){ // 读操作
int len;
char buf[1024];
while(1){
len = read(sockfd, buf, sizeof(buf));
if(len == 0)
break;
write(STDOUT_FILENO, buf, len);
}
close(sockfd);
}
// 客户端
int client(int port, const char *cli_addr)
{
int sockfd;
sockfd = Socket(0);
Connect(sockfd, port, cli_addr);
// 通过创造进程实现IO分离
pid_t pid;
if((pid = fork()) < 0){
fprintf(stderr, "fork error\n");
exit(1);
}
else if(pid == 0)
Write(sockfd); // 向套接字中写数据
else
Read(sockfd); // 从套接字中读数据
return 0;
}
完整代码 : fork_client.c
服务端 :
./a.out 1 8080 127.0.0.1
客服端 :
./a.out 2 8080 127.0.0.1
可以看到我们已经完美的解决了客服端阻塞的问题. 同时也学会了IO分离的技巧以及带来的好处.
小结
本节使用 IO分离 的技巧解决了客服端阻塞的问题, 这样我们也能写出一个完整的基本没有bug 的C/S了. 接下来将学习IO复用继续修改我们的代码使其程序更加高效.
- IO分离
- IO分离的优势