转自: http://blog.csdn.net/wlh_flame/article/details/6381780
(评论:虽然问题不大,但是新手基本会遇到这些问题的,所以还很重要)
——记录一些小问题,陷阱
1. 调用bind时,如果地址是0(就是INADDR_ANY那个宏),就绑定本地所有IP,如果端口是0,就随机选择一个可用的端口(想要知道具体端口,可以调用getsockname查看)
2. send recv 与 read write 的区别主要在于:send 和 recv 可以设置一些 flag,而 read 和 write 则没有
3. 阻塞与非阻塞:调用 API 时是否需要等待完成时才返回
4. send/sendto 的一般返回情况:
非阻塞或超时——根据当前缓冲区空闲长度,尽力向套接字缓冲区拷贝,完成了多少算多少,立返回
阻塞——完成所有数据的拷贝,才返回。如果现在空闲区大小不够,会等待至空闲区够了并拷贝,完成拷贝后返回
tcp 是数据流,所以 send 的数据可以很大,而不会返回消息过长的错误;
udp 因为仅组装成一个数据报,不能够分开多次发送,所以如果发送的数据量大于缓冲区长度,会立即返回消息过长的错误。
非阻塞的发送可以是套接字级别的,通过 setsockopt 设置;也可以是消息级别的,通过 send 调用时的 MSG_DONTWAIT 标记
— 可以用异步方式,将套接字设置为非阻塞,但是需要注意检查 send 函数的返回值,验证 recv 收到的包的完整性
5. udp 下也可以使用 connect,同 tcp 相比,并没有握手过程,只是设定了套接字数据结构里面的远端地址部分
6. 在 tcp 套接字被 shutdown 后,调用读写该套接字的 API 除了返回 EPIPE 错误外,还会产生 SIGPIPE 信号(默认操作是退出进程)。可以在调用 API 时,通过 MSG_NOSIGNAL 标记来告知内核,无需发出这一信号。
7. udp 收包时,如果缓冲区大小比包长要小,那其行为是未定义的,建议最好一次收个够
8. tcp 发包时,担心因为包太小无法即时发送出去的话,可以设置 TCP_NODELAY 套接字选项
9. udp 发包过快会造成本地或远端被“淹没”。考虑一个 udp 服务器,如果接收缓冲区满了,此时还有 udp 数据到达,则协议栈会丢弃新到达的数据。(仅从机器负载去看,难以断定是否会“淹没”,只能估算;淹没发生时,进程通常忙于干其他事情或被阻塞,因此从可控的角度来讲,只好尽量降低 CPU 负载、尽量采用异步)
udp 丢包的几种情况:网络导致,缓冲区长度不够长导致(rmem_max、SOL_SNDBUF),缓冲区已满
10. tcp connect 时,默认超时因系统不同而不同,一般都要那么几分钟,如果想要主动控制这个超时,有几种方法:修改内核参数、使用异步的 select 和 epoll 查看是否返回套接字可写,如果是 linux 环境,那么 SOL_SNDTIMEO 选项也能起作用。
11. linux下多个子进程可以竞争accept同一个套接字,进程/线程池基本就是这样一种思路,先fork几个再让它们都竞争accept。监视客户端的数量,可以动态地增加、减少进程数。
12. 异步connect时,如果超时会返回一个出错事件,错误码被置为EINPROGRESS
13. 编写一个完善的服务器,要考虑:常见信号是否已经捕获并处理(如SIGSEGV、SIGCHLD、SIGPIPE、SIGTERM、SIGINT等),系统API的返回值是否进行了合理的检查,日志体系是否完善。如果使用了epoll,还要对错误类事件统一处理。
14. 对TCP的情况,如果使用KEEPALIVE选项,协议栈会探测链接的对端是否有响应,如果没有的话会返回套接字可读,receive返回错误。