Socket 错误分析及错误码

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的端口不可达通知。 
需要进一步验证。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用Java Socket API编写的简单协议分析器代码示例,仅供参考: ```java import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.util.StringTokenizer; public class ProtocolAnalyzerServer { public static void main(String[] args) throws IOException { int portNumber = 12345; try ( ServerSocket serverSocket = new ServerSocket(portNumber); Socket clientSocket = serverSocket.accept(); BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); ) { String inputLine; while ((inputLine = in.readLine()) != null) { StringTokenizer st = new StringTokenizer(inputLine, ","); String command = st.nextToken(); String arg1 = st.nextToken(); String arg2 = st.nextToken(); // 解析数据包中的各个字段,并在控制台上打印出这些字段的值 System.out.println("Command: " + command); System.out.println("Argument 1: " + arg1); System.out.println("Argument 2: " + arg2); } } catch (IOException e) { System.out.println("Exception caught when trying to listen on port " + portNumber + " or listening for a connection"); System.out.println(e.getMessage()); } } } ``` 此示例代码假设客户端将每个数据包作为以逗号分隔的字符串发送,并且每个数据包包含三个字段:命令、参数1和参数2。在while循环中,程序使用StringTokenizer类将输入行分解为这三个字段,并将它们分别存储在字符串变量中。然后,程序将这些字段的值打印到控制台上。 当然,这只是一个简单的示例,实际的协议分析器需要更多的代码来处理复杂的数据包、错误处理等问题。但是,这个示例可以帮助您了解如何使用Java Socket API构建一个简单的协议分析器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值