网络编程的异常及处理
网络编程不只是编写网络、主机、进程都正常时能良好工作的进程,更重要的是客户主机崩溃、客户进程崩溃网络异常时怎么处理。
accept被信号中断
accept以及套接字上的I/O可能被信号打断,并返回EINTR作为结果,必须处理该返回值并且适当时候再次调用。
for(;;)
{
if(connfd = accept(listenfd, (struct sockaddr*)addr, sizeof(addr)) < 0)
{
if(connfd == EINTR)
{
continue;
}
else
{
printf("accept error\n");
}
}
}
connect被信号中断
connect被信号打断,返回EINTR作为结果,不能对该socket再次调用connect,需要使用select等待连接完成。
accept返回前连接夭折
estiblished状态的连接在被accept取走之前夭折,对于这种错误,不同的系统有不同的处理,大多数是返回一个错误结果给进程,SVR4返回EPROTO(协议错误),posix指出必须返回ECONNABORTED(软件引起的连接错误),在返回ECONNABORTED错误时,重新调用accept即可。
也有的操作系统会直接清除掉这种夭折的连接,服务器进程根本不会发现夭折的连接。
echo回响程序处理服务器端异常终止
客户端进程阻塞于终端输入时,服务端进程异常终止,三次握手终止连接,客户端把FIN追加到clifd的读取缓冲区末尾。
客户从终端输入字符,通过clifd发送给服务端(由于服务端已关闭,会回复一个RST给客户端)并调用read获取结果。
此时FIN已在读取缓冲区,而RST还在网络传输的过程中,因此read返回0,代表对端已关闭,而不是读取到RST的复位消息。
为了避免这种场景的RST消息,客户端需要使用多路复用同时监听多个输入:套接口和终端输入。这样就能早先一步获取FIN消息,避免连接终止后还向对端发送消息。
SIGPIPE
进程向一个接收到RST的套接字写入消息时,内核会给进程发送一个SIGPIPE信号。
完整的过程是:进程向一个接收到FIN的套接字写入消息,接收到RST应答,此时再次写入,内核会给进程发送一个SIGPIPE信号。
此信号的缺省动作是终止进程,不产生core文件。
不管进程是否捕捉该信号,写操作都会返回EPIPE错误。
SIGPIPE的处理
SIGPIPE缺省是终止进程,并且不产生core文件,这就无从跟踪和发现问题。
对SIGPIPE的处理可以是:
1.设置SIG_IGN,无视该信号,并默认write会处理EPIPE错误。
2.信号处理函数中处理该错误,需要知道的是,信号处理函数无法判断是哪个套接字出了错误,如果确实要知道哪个套接字出了错误,还是需要在write返回后处理EPIPE错误。
服务器主机崩溃
当客户端和服务器之间建立连接后,服务器主机崩溃,此时通过网络发往服务器主机的消息不会得到任何回应。
此时可能返回的错误有两种
1.客户端持续重传数据分节,一直没有得到回应,此时应返回ETIMEOUT错误。
2.如果中间路由器判断服务器主机不可达,并返回一个ICMP错误,则错误是EHOSTUNREACH或ENETUNREACH。
通过write的返回值,可以得知错误原因,但是通常需要等待很长一段时间。
快速检测服务器主机崩溃
给套接字加上一个超时时间
在不主动发送消息的时候检测服务器主机崩溃
心跳消息
服务器主机崩溃后重启
如果服务器主机崩溃后并重启,在崩溃期间客户没有发送消息给服务器,那么客户端是不知道服务器曾经崩溃过的(如果不使用套接字选项SO_KEEPALIVE),客户端给服务器发送消息。
服务器崩溃并重启后丢失了所有的连接信息,对于接收到的消息都以RST响应,导致客户端收到ECONNRESET错误。
如果客户端检测服务器的主机崩溃错误,那应该设置SO_KEEPALIVE。
服务器主机关机
UNIX系统关机时,由INIT进程给所有进程发送SIGTERM信号(我们可以捕捉此信号),然后等待一段时间,然后给所有进程发送SIGKILL信号。进程终止时,所有打开的描述字都将关闭。
对于服务器主机关机,客户最好使用多路复用及时检测到套接字的关闭。
网络数据格式
1.转换所有格式的数据为字符串消息
2.明确指定字节序,在服务器和客户端使用同样的字节序。