socket执行accept函数时没有进入阻塞状态,而是陷入了无限循环
接着前两天继续看《VC深入详解》的网络编程部分,这次我快速看了遍书上的函数以及套接字C-S模型,然后自己从0开始写了个简单的服务端,结果发现一直在输出
而明明我还没有写客户端程序,由于打印的代码只有一处,在如下的while循环里
- while (true)
- {
- /* 5. 接收客户端发送的连接请求 */
- SOCKET sockConnect = accept(sockServer, (SOCKADDR*)&addrClient, &len);
- /* 6. [发送/接收]数据 */
- char sendBuf[BUFSIZ];
- char ipBuf[INET_ADDRSTRLEN];
- sprintf(sendBuf, "Welcome [IP: %s] to Server",
- inet_ntop(AF_INET, &addrClient.sin_addr, ipBuf, sizeof(ipBuf)));
- send(sockConnect, sendBuf, sizeof(sendBuf), 0);
- char recvBuf[BUFSIZ];
- recv(sockConnect, recvBuf, BUFSIZ, 0);
- printf("Receive Data: %s\n", recvBuf);
- /* 7. 断开连接,关闭套接字 */
- closesocket(sockConnect);
- }
引用《UNIX网络编程》:accept函数由TCP服务器调用,用于从完成连接队列头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。
调试发现,每一次accept函数都成功完成并执行后续代码,所以才会有无限循环打印的现象。仔细对比书上代码和说明,我的accept函数也没有用错,于是头疼了很久。
How often have I said to you that when you have eliminated the impossible, whatever remains, however improbable, must be the truth?
既然我没有得到程序错误的真相,那么说明我没有排除掉所有的错误。回顾之前代码,我跟示例代码的区别就在于我把绑定IP和端口的代码封装起来了。
- /* 3. 绑定套接字到本地的地址和端口 */
- SOCKADDR_IN addrServer = SockAddr(, "127.0.0.1");
- /* 4. 将套接字设为监听模式, 准备接收连接请求 */
- listen(sockServer, SOMAXCONN);
- inline SOCKADDR_IN SockAddr(u_short port, PCSTR ip, int af = AF_INET)
- {
- SOCKADDR_IN sockAddr_In;
- sockAddr_In.sin_family = af;
- sockAddr_In.sin_port = htons(port);
- auto& addr = sockAddr_In.sin_addr.S_un.S_addr;
- int err = inet_pton(af, ip, &addr);
- if (err == 0)
- {
- throw std::runtime_error("IP地址字节序从网络转换到主机出错!");
- }
- else if (err == -1)
- {
- throw std::runtime_error("IP地址输入格式无效!");
- }
- return sockAddr_In;
- }
但是错误并没有出在我的SockAddr函数,而是在于我没有绑定!漏掉了关键的一行代码
- bind(sockServer, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));
我仅仅只是创建了一个包含协议家族、IP地址、端口号的结构体SOCKADDR_IN,虽然也输入了IP和端口信息,但是却没有把SOCKADDR_IN当做SOCKADDR类型(套接字的地址信息)和SOCKET(套接字本身)进行绑定,而没有绑定的后果呢?
继续引用《UNIX网络编程》:如果一个TCP客户或服务器未曾调用bind捆绑一个端口,当调用connect或listen时,内核就要为套接字选择一个临时端口。让内核选择临时端口对于TCP服务器来说非常罕见,因为服务器是通过它们的众所周知端口被大家认识。
调试发现sockConnect(也就是accept的返回值)是4294967295。稍微敏感的就会发现,这是32位无符号整型的上限值,也就是2^32-1,而SOCKET类型实际上就是UINT_PTR(unsigned int表示的指针,也就是32位地址)
- typedef UINT_PTR SOCKET;
再仔细看看《UNIX网络编程》上关于accept函数的解释
若出错则为-1,UNIX上的accept是返回int,而windows上则是相当于返回unsigned int,-1的二进制表示就是每一位都为1,对应unsigned int的上限值(UINT_MAX)。所以刚才并不是每次都连接成功,而是每次都连接出错(因为试图用一个未绑定地址的服务器socket来接收客户的连接请求),返回错误码-1。如果accept正常,则是有客户连接则返回一个表示连接的socket,没有客户连接则使进程睡眠直到有客户连接(即阻塞)。
总结下来,根本原因还是socket没有配置好就调用accept函数了,同样,如果注释掉listen那行,也会出现同样的现象。
----------------------------------------------------------粗心的分割线----------------------------------------------------------
最后附上一点感悟,急于求成反而会浪费更多的时间,但也有个好处,如果照着书上代码敲一遍,运行正确,然后再看看每个函数代表什么,也许当时就清楚了,但是后来出错时可能就不知道到底掉到那个坑了。有出错经验也不错。《UNIX网络编程》确实是本不错的书,对套接字API讲得很详细,像htnol还有inet_addr等转换函数还是看这本书讲得更清楚。
socket执行accept函数时没有进入阻塞状态,而是陷入了无限循环的更多相关文章
- socket的accept函数解析
今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socket用于服务该客户端,那么,这个新的socket到底有没有占用一个新的端口? 讨论完后,才发现,自己虽然 ...
- 网络编程socket之listen函数
摘要:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程.在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被 ...
- 转: 由socket的accept说开去
from: http://ticktick.blog.51cto.com/823160/779866 今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socke ...
- socket编程之accept()函数【转载】
名称 accept() 接收一个套接字中已建立的连接 使用格式 #include <sys/types.h> #include <sys/socket.h> int accep ...
- 写JS自执行函数时要注意的
JS是非强类型语言,且IDE也不够智能,所以经常会在语句结束时漏写了分号,一般情况下这是不影响运行的, 但如果后面跟上的是一段自执行的函数,就会报出 "..... is not functi ...
- 面转栅格之ERROR 999999:执行函数时出错
今天进行矢量面转栅格的操作时,总是出现ERROR 999999:执行函数时出错,如下图所示: 刚开始以为是栅格保存的路径太长的问题,后来发现是矢量面的路径问题,我的矢量面是放在自建的图层组下面,如下图 ...
- register_shutdown_function函数详解--脚本退出时执行回调函数
register_shutdown_function — Register a function for execution on shutdown. ps:Registers a callback ...
- Python中网络编程对socket accept函数的理解
在服务器端,socket()返回的套接字用于监听(listen)和接受(accept),这个套接字不能用于与客户端之间发送和接收数据. accept()接受一个客户端的连接请求,并返回一个新的套接字, ...
- jquery 调用函数时加()和不加()的执行顺序是不同的
编写JQUERY(3.0,向下兼容3.0)代码当我们调用一个函数时可以不加括号,但加括号与不加括号是不同的.如下代码: $(function(){ a(b);//先打印a 再打印 b a(b());/ ...
随机推荐
- rsync的服务端和客户端搭建
首先要看看有没有rsync,没有就按装一个rsync 1配置文件 然后创建rsyncd.conf文件,并添加如下内容(文件默认不存在) [root@chensiqi2 backup]# cat /et ...
- mac下的一些mysql操作
#一.从终端进入mysql 不同于windows下的mysql.mac下的mysql安装路径不同,所以操作上会略有不同: 以下操作以默认安装mysql为前提. ##一(1):打开终端后,先设置路径,后 ...
- 我也说说Emacs吧(4) - 光标的移动
在说基本编辑命令之前,我们先加一个小tip,说说如何将函数和键绑定在一起. (define-key global-map [?\C-l] 'recenter-top-bottom) define-ke ...
- 文件处理工具 gif合成工具 文件后缀批量添加工具 文件夹搜索工具 重复文件查找工具 网页图片解析下载工具等
以下都是一些简单的免费分享的工具,技术支持群:592132877,提供定制化服务开发. Gif动图合成工具 主要功能是扫描指定的文件夹里的所有zip文件,然后提取Zip文件中的图片,并合成一张gif图 ...
- python 怎么画图
1 安装matplotlib: 安装方法:http://www.2cto.com/os/201309/246928.html(其中,安装过程中,tar解压怎么解都有问题.然后就删掉再下载一遍) 2 使 ...
- 【解题报告】Codeforces Round #301 (Div. 2) 之ABCD
A. Combination Lock 拨密码..最少次数..密码最多有1000位. 用字符串存起来,然后每位大的减小的和小的+10减大的,再取较小值加起来就可以了... #include<st ...
- 一图说明offsetTop、top、clientTop、scrollTop等
offsetParent:该属性返回一个对象的引用,这个对象是距离调用offsetParent的元素最近的(在包含层次中最靠近的),已进行过CSS定位的容器元素. 如果这个容器元素未进行CSS定位, ...
- AngularX 指令(ngForof)(转载)
该指令用于基于可迭代对象中的每一项创建相应的模板.每个实例化模板的上下文对象继承于外部的上下文对象,其值与可迭代对象对应项的值相关联. NgForOf 指令语法 * 语法糖 <li *ngFor ...
- bzoj 2850 巧克力王国
bzoj 2850 巧克力王国 钱限题.题面可以看这里. 显然 \(x\) \(y\) 可以看成坐标平面上的两维,蛋糕可以在坐标平面上表示为 \((x,y)\) ,权值为 \(h\) .用 \(kd- ...
- Byte.parseByte(String s,int radix)的解释
1. 由 基本数据型态转换成 String String 类别中已经提供了将基本数据型态转换成 String 的 static 方法 也就是 String.valueOf() 这个参数多载的方法 有下 ...