本次博客主要总结参考《Unix网络编程》卷一前四章的知识,对TCP一对一通信进行重新改造和分析,经典就是经典,无可替代!
一、为什么使用包裹函数
任何现实世界的程序都必须检查每个函数调用是否返回错误。当某个函数发生错误时,就调用我们自己的err_quit或err_sys函数输出一个错误消息并终止程序的运行(当然有时候并非简单的终止程序运行,还需要检查问题所在:系统调用是否被中断了)。
既然发生错误时终止程序的运行时普遍的情况,我们可以通过定义包裹函数(wrapper function)来缩短程序。每个包裹函数完成实际函数的函数调用,检查返回值,并在发生错误时终止进程。我们约定包裹函数名是实际函数名的首字母大写形式。例如
listenfd = Socket(AF_INET,SOCK_STREAM,0);
包裹函数Socket定义为:
int Socket(int family,int type,int protocol)
{
int n;
if ((n = socket(family,type,protocol)) < 0)
err_sys("socket error");
return n;
}
因此,在UNP课程后面的所有例子中,我们建议使用包裹函数的形式,不仅缩短了代码,有利于开发者看清楚具体的功能实现;而且考虑了对于每一步的出错信息及处理,使代码更具有健壮性。
二、Unix errno值
只要一个Unix函数(例如某个套接字函数)中有错误发生,全局变量errno就被置为一个指明该错误类型的正值,函数本身则通常返回-1。
errno的值是只在函数发生错误时设置。如果函数不返回错误,errno的值就没有定义。errno的所有正数错误值都是常值,具有以“E”开头的全大写字母名字,并通常在头文件sys/errno.h定义。值0不表示任何错误。
在全局变量中存放errno值对于共享所有全局变量的多个线程并不适合。
三、TCP通信流程
基于TCP(面向连接)的socket编程,分为服务器端和客户端
服务器端的流程如下:
(1)创建套接字(socket)
(2)将套接字绑定到一个本地地址和端口上(bind)
(3)将套接字设为监听模式,准备接收客户端请求(listen)
(4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
(5)用返回的套接字和客户端进行通信(send/recv 、write/read)
(6)返回,等待另一个客户请求。
(7)关闭套接字。
客户端的流程如下:
(1)创建套接字(socket)
(2)向服务器发出连接请求(connect)
(3)和服务器端进行通信(send/recv 、write/read)
(4)关闭套接字