本文共 5334 字,大约阅读时间需要 17 分钟。
平台:xp sp3+vc6。 测试方法: retval = function(....) if(retval == SOCKET_ERROR) r = WSAGetLastError(); 各种情况下的返回值由retval取得。 错误号由r取得。 错误查询使用vc6自带的tool:“Error Lookup” 1。socket SOCKET socket( int af , int type , int protocol ); 》》af,通常为AF_INET 使用AF_ISO等其他地址族标识,而非AF_INET。 返回:-1。 错误:10047(使用了与请求的协议不兼容的地址) 》》type,通常为SOCK_STREAM或SOCK_DGRAM 头文件中定义的只有如下几种标准类型: #define SOCK_STREAM 1 /* stream socket */ #define SOCK_DGRAM 2 /* datagram socket */ #define SOCK_RAW 3 /* raw-protocol interface */ #define SOCK_RDM 4 /* reliably-delivered message */ #define SOCK_SEQPACKET 5 /* sequenced packet stream */ 使用非如上定义的类型。 返回:-1。 错误:10044(在这个地址家族中不存在对指定的插槽类型的支持) 》》protocol,通常为0 type = SOCK_STREAM,protocol = 6 正常 type = SOCK_STREAM,protocol = 7 返回:-1 错误号:10043(请求的协议还没有在系统中配置,或者没有它存在的迹象) type = SOCK_DGRAM,protocol = 17 正常 type = SOCK_DGRAM,protocol = 19 返回:-1 错误号:10043(请求的协议还没有在系统中配置,或者没有它存在的迹象) 》》结论 Socket暂时只支持AF_INET协议族。 对非标准的套接字类型不支持。 协议号参数可以为0,则使用与套接字类型相应的协议号; 否则,协议号参数必须与相应的套接字类型相同。 2。bind int bind( SOCKET s , const struct sockaddr FAR* name , int namelen ); 》》s 在没有用socket申请资源的套接字上操作。 返回:-1 错误号:10038(在一个非套接字上尝试了一个操作) 》》name,通常使用AF_INET地址族、INADDR_ANY(0)地址 a、在local结构中,sin_family成员赋值为AF_OSI, 返回: -1 错误码:10047(使用了与请求的协议不兼容的地址) b、在local结构中,sin_addr成员赋值为本计算机的IP地址, 正常 c、在local结构中,sin_addr成员赋值为非本计算机的IP地址,如同小组的另一个同学的IP地址; 返回: -1 错误码:10049( 在其上下文中,该请求的地址无效) d、在local结构中,sin_port成员赋值为135; 返回: -1 错误码:10048(通常每个套接字地址(协议/网络地址/端口)只允许使用一次) 》》namelen,通常为name所指的结构的大小,如sizeof(SOCKADDR_IN) namelen = 10 返回: -1 错误码:10014(系统检测到在一个调用中尝试使用指针参数时的无效指针地址) namelen = 16 返回: 0 正常 namelen = 40 返回: 0 正常 》》结论 可以bind本机拥有的地址(或INADDR_ANY),非本机拥有的地址出错。 bind已经被占用的端口值会出错。 len参数要 大于等于 地址结构实际上所占的长度。 》》思考 因为本机可以有多个IP,所以需要有方法指出从哪个实体接收数据。 当然,提供一种表达“从所有实体接收”的方法是必要的。 在头文件中INADDR_ANY被明确定义为0。 关于bind 已占用的端口 。 是指端口被bind,并且上层还是活的。(不设置复用) 处于TIME-WAIT状态的端口表面上是被占用, 实际上是可以bind成功的,但connect会失败。 详见关于 ,第六条。 3。listen int listen( SOCKET s , int backlog ); 》》s 使用尚未半相关的套接字。(未成功bind的) 返回:-1 错误号:10022(提供了一个无效的参数) 》》backlog 纯引用一段:(无出处) “windows套接字实现中最多只允许服务器同时监听5个套接字。 使用参数0,则系统将把该参数改为1,而使用超过5的值,系统将自动把该参数改为5。” 设置参数值为0,有 1 个客户机可同时与服务器连接 (在vista下有时有2个可以连接,有时有3个可以连接,-_0//) 设置参数值为1,有 1 个客户机可同时与服务器连接 设置参数值为10,有 10 个客户机可同时与服务器连接 》》结论 第一个参数的套接字必须是成功bind过后的。 监听个数为0的话,会自动设置为1。 最大监听个数在XP SP3下可以超过5。 》》问题 如何获得实际的backlog值? MSDN: There is no standard provision to obtain the actual backlog value. 如何结束套接字的监听状态? 首先,close掉是可以的。如果不close呢? 最初猜想backlog为0,-1等特殊值可以达到此效果,结果失败。 求解。 4。accept SOCKET accept( SOCKET s , struct sockaddr FAR* addr , int FAR* addrlen ); 》》s 在没有listen的套接字上面。 返回:-1 错误号:10022(提供了一个无效的参数) 》》addr,输出参数,一般不填 单机测试,填上本机的IP和某个端口号。 结果:无法限制所接收的地址,执行完后addr中存放实际的地址。 》》addrlen 同bind 》》结论 主套接字必须处于监听状态。 在地址字段填上任何值不能限制接受的连接。 len参数要求所携带的值大于等于16。(AF_INET地址结构的长度) 5。recv int recv( SOCKET s , char FAR* buf , int len , int flags ); 对于服务器,一般是 ns = accept(s , &addr , &len) ; 》》s,一般是用上面accept正常返回的值 在没有accept的从套接字上操作。(上面的ns) 返回:-1 错误号:10038(在一个非套接字上尝试了一个操作) 在主套接字上操作。(上面的s) 返回:-1 错误号:10057(由于套接字没有连接并且 (当使用一个 sendto 调用发送数据报套接字时)没有提供地址, 发送或接收数据的请求没有被接受。 ) 》》buf,要求指向一个有效的缓冲区 如果指向的无效的内存区域 返回: -1 错误号:10014(系统检测到在一个调用中尝试使用指针参数时的无效指针地址) 》》len,发送的字节数 len过长可能造成缓冲区溢出。 这个属于编程中的普遍考虑问题,不是socket函数特有。 》》flags,一般用0 设置MSG_PEEK标志后,接收但不移除数据。 (再次接收可得到相同的数据) 》》结论 对服务器来说,必须传递成功accept之后返回的套接字。 缓冲区指针所指位置必须有效。 缓冲区长度参数不可超过实际准备的缓冲区长度。 MSG_PEEK标志在接收的时候将保留数据。 6。send int send( SOCKET s , const char FAR* buf , int len , int flags ); 》》s,同recv 在没有accept的从套接字上操作。(上面的ns) 返回:-1 错误号:10038(在一个非套接字上尝试了一个操作) 在主套接字上操作。(上面的s) 返回:-1 错误号:10057(由于套接字没有连接并且 (当使用一个 sendto 调用发送数据报套接字时)没有提供地址, 发送或接收数据的请求没有被接受。 ) 》》buf 必须指向有效缓冲区,同recv 》》len 必须和要发送的数据长度一致。 》》结论 除flag可选项不同外,和recv一致。 7。closesocket int closesocket( SOCKET s ); 》》s 在申请套接字资源(调用socket)之前closesocket 返回: -1 错误号:10038(在一个非套接字上尝试了一个操作) 再已经closesocket的套接字上closesocket 返回: -1 错误号:10038(在一个非套接字上尝试了一个操作) 》》结论 s必须是有效打开的套接字。 不得重复关闭。 8。connect int connect( SOCKET s , const struct sockaddr FAR* name , int namelen ); 》》没有对端响应的情况 在没有运行服务器的情况下,connect是否会一直阻塞? 结果:等待一定时间后返回错误。 返回: -1 错误码:10061(由于目标机器积极拒绝,无法连接) 》》s 没有使用过bind的套接字。 成功连接。 产生隐式绑定, 。 》》name 使用一些特殊的地址来测试。 a、 使用远端点IP地址为INADDR_ANY测试。 返回:-1 错误号:10049(在其上下文中,该请求的地址无效) b、 使用远端点IP地址为10.1.1.255广播地址。 返回: -1 错误号:10060(由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败) 》》namelen 同bind等需要传递地址结构长度的函数 》》结论 服务器必须启动listen。 可以不建立本地半相关,则进行隐式绑定。 客户不可以与INADDR_ANY主动相连,立即返回报错。 客户不可以与广播地址连接,会等待很久,返回失败。 》》讨论 10060和10061两种错误不同。 其中10061解释为目标机器积极拒绝,返回错误很快(秒级)。 10060的情况,返回错误需要很长时间(几十秒级)。 此处值得深入研究,两种情况下的抓包应该不一样。 9。recvfrom int recvfrom( SOCKET s , char FAR* buf , int len , int flags , struct sockaddr FAR* from , int FAR* fromlen ); 》》缺少半相关 在没有bind的套接字上面,直接recvfrom。 返回:-1 错误号:10022(提供了一个无效的参数) 》》填写from结构 向其中填入非对端使用的地址或端口。 正常接收,并且from内置对端地址信息。 》》结论 必须先进行本地半相关,指定端口,才能够接收。 无法通过recvfrom的地址结构限制接收的地址和端口。 10。sendto int sendto( SOCKET s , const char FAR* buf , int len , int flags , const struct sockaddr FAR* to , int tolen ); 》》缺少半相关 在没有bind的套接字上面,直接sendto。 成功。 返回发送的数据个数。 》》不存在的对端实体 sendto到一个不存在的实体(to结构) 返回:发送的字符数 错误号:无 紧接着调用recvfrom 结果:没有阻塞,直接返回 返回:-1 错误号:10054 (远程主机强迫关闭了一个现有的连接。) 》》结论 可以在未本地半相关的情况下发送数据。 由系统随机选择端口。 可以向不存在的远端点发送数据, 本地仍然报告发送的字节数( 不管有没有人接收 )。 一般情况下,没有数据的时候recvfrom会阻塞。 但是当给不存在的对端发送过数据后,会收到错误报告, 紧接着的一次recvfrom会立即返回失败。(后面的仍然阻塞) 》》思考 SOCK_DGRAM类型的服务, 无法为用户确保数据的正常交付。 但是通过recvfrom返回的错误, 可以对发送情况作出一定的判断。 这也启示在使用SOCK_DGRAM时候的编程框架要考虑下, 当recvfrom错误的时候,判断一下错误号,再进一步处理。 猜测 当对端没有bind的时候,使用icmp的端口不可达通知。 需要进一步验证。 转载于:https://my.oschina.net/apoptosis/blog/123285