Socket编程中常见的错误

(一)  accept句柄耗尽:

服务器每次accept一个链接之后,都会分配新的socket资源。Linux下对每个进程所能使用的文件句柄数是有限制的,默认是1024。扣除stdin,stdout,stderr,只有1021个句柄可用。一旦出现了accept的连接数超过了这个限制后就会很尴尬:accept返回-1,errno = 24,即EMFILE。如果采用Select、poll 或者epoll LT方式,系统会不停通知你,但是你没法拿到那个fd,也就不能处理,甚至连close都不能。只能尴尬地让cpu跑在100%,期待着某个客户端断线,某个句柄恰好被close,然后才能处理之。

解决办法有两种,

1,   准备一个空闲的nullfd = open(“/dev/null”), close(nullFd),然后accept,close(socketFd),最后在nullfd = open(“/dev/null”),缺点,并不是线程安全的。

2,   直接在代码中限制最大允许连接数,一旦超过则直接踢出。缺点,需要额外的关注。

(二)  Socket资源不释放

服务器未能有效地检测和处理socket关闭事件,导致最后socket资源被耗尽,再也不能连入新的连接。

解决办法:keepalive、网络连接心跳检测,定时关闭不活动的连接。

(三)  发送缓冲使用不当,数据包乱序。

在上篇文章中的例子程序就存在这样的问题:当有数据要发送时,无视发送缓冲直接调用send函数。在同一连接中会发生数据A发送了一部分,又发送数据B的一部分,在发送数据A的一部分。令接收者无所适从。

解决办法:从不直接调用send发送数据,应将发送数据放入发送缓冲中处理。

(四)  网络串包:

将A的信息发给了B。通常发生在服务器用socketFd1接受A的请求,服务器处理中。A掉线,服务器close(socketFd1)。这时B再上线,POSIX标准要求每次打开文件的时候必须使用当前最小可用的文件描述符号,于是B使用了socketFd1句柄。服务器就将A的应答发送给了B.

解决办法:1,socket错误处理时不能直接close了事。2,处理应答时不能直接将数据包丢给socket句柄,要建立某种机制确认socket句柄是你想发送的那个。

(五)服务器端太多的TIME_WAIT

 主动关闭的Socket端会进入TIME_WAIT状态,并且持续2MSL时间长度,MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒,因而,TIME_WAIT状态一般维持在1-4分钟。如果每次关闭socket的动作都由服务器发起,那么TIME_WAIT状态就会留在服务端,服务端的内核就需要维护更多的状态。收到ip包,做hash运算,hlist冲突的概率更大。

解决办法:使用shutdown(sockfd, SHUT_WR)通知客户端,由客户端执行close,将TIME_WAIT留在客户端。

 (六)没有处理SIGPIPE引起服务器崩溃。

当一个进程向某个已收到RST的Socket执行写操作时,内核将向进程发送一个SIGPIPE信号。该信号的默认行为是终止进程。

解决办法:忽略该信号。

struct sigaction act;
act.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &act, NULL) == 0) {
    LOG("SIGPIPE ignore");
}

 (七)阻塞I/O没有处理EINTR、非阻塞I/O没有处理EAGAIN

对于慢系统调用,像accept, receive这样有可能无法返回的函数的进程一旦捕获了某个信号,并且相应的信号处理函数返回时,该系统调用有可能返回一个EINTR错误。

非阻塞模式下调用了阻塞操作,在该操作没有完成就返回EAGAIN。EAGAIN不会破坏socket的同步,不用管它。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值