客户端程序设计范式
1,基本的TCP客户程序
存在两个问题:
1.进程在被阻塞以等待客户输入期间,看不到诸如对端关闭等网络事件;
2.停-等模式,批处理效率极低;
1 |
|
2,迭代客户程序
通过select使进程能够在等待用户输入期间得到网络时间通知,
存在问题:
1.然而不能正确处理批量输入(使用shutdown解决,即告诉已经完成数据发送,但保持在路上传输的数据);
2.fgets只返回第一行,其余 输入行仍在stdio缓冲区,select随后再次进入等待新工作中,所以select不要和标准IO同时使用;
3.使用的是阻塞IO,如调用writen,如果套接字发送缓冲区已满,writen会阻塞;
1 |
|
shutdown修改版
使其正确处理批量输入,还废弃以文本为中心的代码,改为针对缓冲区的操作。
1 |
|
3,使用非阻塞I/O实现的客户程序
通过设置套接字选项来实现非阻塞。阻塞的话就直接停在那等条件成熟,非阻塞就继续其他工作,或立刻返回相应条件。
可能阻塞的四类:
1.输入操作,如read, readv, recv, recvfrom,recvmsg
2.输出操作,如write,writev,send,sendto,sendmsg
3.接受外来连接,accept
4.发起外出连接,connect,这可以使三路握手叠加在其他处理上,同时建立多个连接
非阻塞读写例子
使用自己的缓冲区,使其不用阻塞于套接字缓冲区或则标准缓冲区中,但新建自己的缓冲区会使代码复杂。如客户端一部分缓冲用于发送数据,部分用于接受数据,两者同时进行。详细见书。
1 | /* include nonb1 */ |
4,使用子进程的客户程序
单个子进程处理客户到服务器数据,父进程处理服务器到客户数据的程序
注意:下面有提到父进程不能调用close,改为shutdown
我们所期望的是父进程获取完标准终端的数据,写入套接字后close套接字,并退出,服务器端接收完数据检测到EOF(表示数据已发送完),也关闭连接,并退出。子进程读取完服务器端响应的数据,并退出。然而,事实会是这样子的嘛,其实不然!父进程close套接字后,套接字对于子进程来说仍然是可读和可写的,尽管子进程永远都不会写入数据。因此,此socket的断连过程没有发生,因此,服务器端就不会检测到EOF标识,会一直等待从客户端来的数据。而此时子进程也不会检测到服务器端发来的EOF标识。这样服务器端和客户端陷入了死锁。如果用shutdown代替close,则会避免死锁的发生。
1 |
|
5,使用线程的客户程序
使用线程代替进程,过程同上
1 |
|
总的运行时间
1,354.s
2,12.3s
3,6.9s(代码复杂)
4,8.7s(fork在多进程下运行代价高)
5,8.5s(推荐)