今天给大家唠唠我最近捣鼓网络编程时遇到的一个坑,关于“socket accept”的,说起来都是泪。
我按照网上的教程,兴冲冲地创建一个 socket 服务器端,代码写得那叫一个顺溜:先用 socket() 函数创建一个 socket,然后用 bind() 函数绑定一个端口,接着 listen() 开启监听,用 accept() 函数等待客户端连接。心里美滋滋的,感觉一切尽在掌握。
结果,运行起来,客户端死活连不上。我当时就懵,这是咋回事?我左看看右看看,代码没问题!
于是我开始疯狂地排查。我想到的是防火墙。会不会是防火墙把我的端口给拦住?我赶紧打开防火墙设置,把我的程序加到白名单里,心想这下总该行。还是不行!客户端依然连不上。
我又怀疑是不是端口被占用。我用命令查一下,发现端口确实没被占用。这下更奇怪,到底哪里出问题?
- 我甚至怀疑是不是我的电脑网络有问题,尝试重新安装TCP/IP协议。
- 还试用360卫士的lsp修复工具,但也没发现问题。
我开始怀疑是不是代码逻辑有问题。我仔细检查代码,特别是 accept() 函数部分。我发现网上有说 accept() 是个慢系统调用,如果在等待连接时被信号中断,会导致 accept() 失败,错误码被设置为 EINTR。为解决这个问题,我试着把 accept() 函数放在一个循环里,如果返回失败且错误码是 EINTR,就用 continue 重新执行 accept()。这一改,诶,好像有点效果,但还是不稳定,有时能连上,有时又连不上。
后来我又解到可以用 select() 函数来检测 socket 是否有可用的连接,避免 accept() 阻塞。我试着把 accept() 改成非阻塞模式,然后用 select() 监听可读事件,有连接来再调用 accept()。这一改,问题终于解决!客户端可以稳定地连接到服务器端。另外我还尝试使用多线程的方式,把服务器端的逻辑放在一个单独的线程里运行,这样就不会阻塞主线程。这个方法也挺好用的,大家可以试试。
我还发现一个坑。我为调试方便,在循环里频繁地创建和关闭 socket,结果导致系统里出现大量的 TIME_WAIT 状态的连接,最终导致 accept() 失败,提示 “socket: Too many open files”。原来是我的文件描述符用完!我赶紧修改代码,确保每次循环都正确地关闭不再使用的 socket,这个问题也解决。也算是吃次堑,长一智。
经过这回折腾,我对“socket accept”的理解更深刻。总结起来就是:
- 要处理好信号中断导致的 accept() 失败。
- 可以使用 select() 或者多线程来避免 accept() 阻塞。
- 要记得及时关闭不再使用的 socket,避免文件描述符耗尽。
希望我的经历能帮到大家,少走一些弯路。网络编程这玩意儿,坑是真的多,大家一定要细心!