Socket 错误分析及错误码
平台: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会失败。
详见关于
TIME-WAIT的笔记
,第六条。
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的端口不可达通知。
须要进一步验证。
欢迎关注本站公众号,获取更多信息