使用socket套接字的一些错误

原文地址:使用socket函数的一些常见错误

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 
    #define SOCK_DGRAM 2 
    #define SOCK_RAW 3 
    #define SOCK_RDM 4 
    #define SOCK_SEQPACKET 5 
    使用非如上定义的类型。 
    返回:-1。 
    错误:10044(在这个地址家族中不存在对指定的插槽类型的支持)

  • protocol,通常为0

    1. type = SOCK_STREAM,protocol = 6 
      正常
    2. type = SOCK_STREAM,protocol = 7 
      返回:-1 
      错误号:10043(请求的协议还没有在系统中配置,或者没有它存在的迹象)
    3. type = SOCK_DGRAM,protocol = 17 
      正常
    4. type = SOCK_DGRAM,protocol = 19 
      返回:-1 
      错误号:10043(请求的协议还没有在系统中配置,或者没有它存在的迹象)
  • 结论

    1. Socket暂时只支持AF_INET协议族。
    2. 对非标准的套接字类型不支持。
    3. 协议号参数可以为0,则使用与套接字类型相应的协议号;否则,协议号参数必须与相应的套接字类型相同。

2.bind

int bind( SOCKET s, const struct sockaddr FAR* name, int namelen );


  • 在没有用socket申请资源的套接字上操作。 
    返回:-1 
    错误号:10038(在一个非套接字上尝试了一个操作)

  • name,通常使用AF_INET地址族、INADDR_ANY(0)地址

    1. 在local结构中,sin_family成员赋值为AF_OSI, 
      返回: -1 
      错误码:10047(使用了与请求的协议不兼容的地址)
    2. 在local结构中,sin_addr成员赋值为本计算机的IP地址,
    3. 在local结构中,sin_addr成员赋值为非本计算机的IP地址,如同小组的另一个同学的IP地址; 
      返回: -1 
      错误码:10049( 在其上下文中,该请求的地址无效)
    4. 在local结构中,sin_port成员赋值为135; 
      返回: -1 
      错误码:10048(通常每个套接字地址(协议/网络地址/端口)只允许使用一次)
  • namelen,通常为name所指的结构的大小,如sizeof(SOCKADDR_IN)

    1. namelen = 10 
      返回: -1 
      错误码:10014(系统检测到在一个调用中尝试使用指针参数时的无效指针地址)
    2. namelen = 16 
      返回: 0 
      正常
    3. namelen = 40 
      返回: 0 
      正常
  • 结论

    1. 可以bind本机拥有的地址(或INADDR_ANY),非本机拥有的地址出错。
    2. bind已经被占用的端口值会出错。
    3. len参数要大于等于地址结构实际上所占的长度。
  • 思考

    1. 因为本机可以有多个IP,所以需要有方法指出从哪个实体接收数据。
    2. 当然,提供一种表达“从所有实体接收”的方法是必要的。
    3. 在头文件中INADDR_ANY被明确定义为0。
    4. 关于bind已占用的端口。是指端口被bind,并且上层还是活的。(不设置复用)处于TIME-WAIT状态的端口表面上是被占用,实际上是可以bind成功的,但connect会失败。详见关于TIME-WAIT的笔记,第六条。

3.listen

int listen(SOCKET s, int backlog);


  • 使用尚未半相关的套接字。(未成功bind的) 
    返回:-1 
    错误号:10022(提供了一个无效的参数)

  • backlog 
    纯引用一段:(无出处)

“windows套接字实现中最多只允许服务器同时监听5个套接字。使用参数0,则系统将把该参数改为1,而使用超过5的值,系统将自动把该参数改为5。”

  设置参数值为0,有1个客户机可同时与服务器连接(在vista下有时有2个可以连接,有时有3个可以连接,-_0//)设置参数值为1,有1个客户机可同时与服务器连接设置参数值为10,有10个客户机可同时与服务器连接

  • 结论

    1. 第一个参数的套接字必须是成功bind过后的。
    2. 监听个数为0的话,会自动设置为1。
    3. 最大监听个数在XP SP3下可以超过5。
  • 问题

    1. 如何获得实际的backlog值? 
      MSDN: There is no standard provision to obtain the actual backlog value.

    2. 如何结束套接字的监听状态? 
      首先,close掉是可以的。如果不close呢?最初猜想backlog为0,-1等特殊值可以达到此效果,结果失败。求解。

4.accept

SOCKET accept( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );


  • 在没有listen的套接字上面。 
    返回:-1 
    错误号:10022(提供了一个无效的参数)

  • addr,输出参数,一般不填 
    单机测试,填上本机的IP和某个端口号。 
    结果:无法限制所接收的地址,执行完后addr中存放实际的地址。

  • addrlen 
    同bind

  • 结论

    1. 主套接字必须处于监听状态。
    2. 在地址字段填上任何值不能限制接受的连接。
    3. len参数要求所携带的值大于等于16。(AF_INET地址结构的长度)

5.recv

int recv( SOCKET s, char FAR* buf, int len, int flags ); 
对于服务器,一般是ns = accept(s , &addr , &len) ;

  • s,一般是用上面accept正常返回的值

    1. 在没有accept的从套接字上操作。(上面的ns) 
      返回:-1 
      错误号:10038(在一个非套接字上尝试了一个操作)

    2. 在主套接字上操作。(上面的s) 
      返回:-1 
      错误号:10057(由于套接字没有连接并且 
      (当使用一个 sendto 调用发送数据报套接字时)没有提供地址, 
      发送或接收数据的请求没有被接受。 )

  • buf,要求指向一个有效的缓冲区 
    如果指向的无效的内存区域 
    返回: -1 
    错误号:10014(系统检测到在一个调用中尝试使用指针参数时的无效指针地址)

  • len,发送的字节数 
    len过长可能造成缓冲区溢出。 
    这个属于编程中的普遍考虑问题,不是socket函数特有。

  • flags,一般用0 
    设置MSG_PEEK标志后,接收但不移除数据。 
    (再次接收可得到相同的数据)

  • 结论

    1. 对服务器来说,必须传递成功accept之后返回的套接字。
    2. 缓冲区指针所指位置必须有效。
    3. 缓冲区长度参数不可超过实际准备的缓冲区长度。
    4. MSG_PEEK标志在接收的时候将保留数据。

6.send

int send( SOCKET s, const char FAR* buf, int len, int flags );

  • s,同recv

    1. 在没有accept的从套接字上操作。(上面的ns) 
      返回:-1 
      错误号:10038(在一个非套接字上尝试了一个操作)

    2. 在主套接字上操作。(上面的s) 
      返回:-1 
      错误号:10057(由于套接字没有连接并且 
      (当使用一个 sendto 调用发送数据报套接字时)没有提供地址, 
      发送或接收数据的请求没有被接受。 )

  • buf 
    必须指向有效缓冲区,同recv

  • len 
    必须和要发送的数据长度一致。

  • 结论 
    除flag可选项不同外,和recv一致。

7.closesocket

int closesocket( SOCKET s );

  • s

    1. 在申请套接字资源(调用socket)之前closesocket 
      返回: -1 
      错误号:10038(在一个非套接字上尝试了一个操作)

    2. 再已经closesocket的套接字上closesocket 
      返回: -1 
      错误号:10038(在一个非套接字上尝试了一个操作)

  • 结论 
    s必须是有效打开的套接字。 
    不得重复关闭。

8.connect

int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );

  • 没有对端响应的情况 
    在没有运行服务器的情况下,connect是否会一直阻塞? 
    结果:等待一定时间后返回错误。 
    返回: -1 
    错误码:10061(由于目标机器积极拒绝,无法连接)


  • 没有使用过bind的套接字。 
    成功连接。 
    产生隐式绑定,相关应用的详细资料。

  • name 
    使用一些特殊的地址来测试。

    1. 使用远端点IP地址为INADDR_ANY测试。 
      返回:-1 
      错误号:10049(在其上下文中,该请求的地址无效)

    2. 使用远端点IP地址为10.1.1.255广播地址。 
      返回: -1 
      错误号:10060(由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败)

  • namelen 
    同bind等需要传递地址结构长度的函数

  • 结论

    1. 服务器必须启动listen。
    2. 可以不建立本地半相关,则进行隐式绑定。
    3. 客户不可以与INADDR_ANY主动相连,立即返回报错。
    4. 客户不可以与广播地址连接,会等待很久,返回失败。
  • 讨论 
    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。 
    成功。返回发送的数据个数。

  • 不存在的对端实体

    1. sendto到一个不存在的实体(to结构) 
      返回:发送的字符数 
      错误号:无

    2. 紧接着调用recvfrom 
      结果:没有阻塞,直接返回 
      返回:-1 
      错误号:10054 (远程主机强迫关闭了一个现有的连接。)

  • 结论

    1. 可以在未本地半相关的情况下发送数据。由系统随机选择端口。

    2. 可以向不存在的远端点发送数据,本地仍然报告发送的字节数(不管有没有人接收)。

    3. 一般情况下,没有数据的时候recvfrom会阻塞。但是当给不存在的对端发送过数据后,会收到错误报告,紧接着的一次recvfrom会立即返回失败。(后面的仍然阻塞)

  • 思考

    1. SOCK_DGRAM类型的服务,无法为用户确保数据的正常交付。但是通过recvfrom返回的错误,可以对发送情况作出一定的判断。

    2. 这也启示在使用SOCK_DGRAM时候的编程框架要考虑下,当recvfrom错误的时候,判断一下错误号,再进一步处理。




  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 基于java实现Socket套接字一对一聊天系统源码+项目说明.zip 该项目是个人毕设项目源码,评审分达到95分,都经过严格调试,确保可以运行!放心下载使用。 该项目资源主要针对计算机、自动化等相关专业的学生或从业者下载使用,也可作为期末课程设计、课程大作业、毕业设计等。 具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现类似其他功能。 简介 Socket,又称套接字,在TCP/IP协议分层架构中,属应用层和传输层之间的一个抽象层(也有的说是属于传输层),它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。 ![在这里插入图片描述](README.assets/01.png) ## 1.2 优点 1. 传输数据时间短,性能高 2. 可以加密,数据安全性强 3. 适合客户端和服务器端之间信息实时交互 4. 传输数据为字节级,传输数据可自定义,数据量小 ## 1.3 缺点 相对于http协议传输,增加了开发量 服务端 1. **创建Socket,bind()绑定地址,listen()监听套接字** > ServerSocket只需要一行代码就可以完成创建、绑定、监听Socket,绑定的地址没填默认就是本机的ip(在源码中InetAddress.anyLocalAddress()方法得出的结论),监听Socket监听的是改Socket的最大连接数,如果队列满时,会出现提示,并拒绝连接(源码的listen(int backlog)方法这样解释)。 ```java ServerSocket serverSocket = new ServerSocket(端口号); ``` 2. **accept()监听连接** > accept()方法侦听要与ServerSocket建立的连接并接受它。该方法会阻塞,直到建立连接。 ``` Socket socket = serverSocket.accept(); ``` 3. **read()读数据** 客户端 1. **创建Socket、connect()连接Socket** 客户端创建Socket,`new Socket(String host, int port);`需要抛出异常,当IP地址错误、端口号错误、服务器`ServerSocket`未启动时,都会抛出异常,即连接失败。 ```java try { Socket socket = new Socket(String host, int port); } catch (Exception e) { e.printStackTrace(); } ``` 2. **write()写数据** > 同服务端 3. **read()读数据** > 同服务端 4. **close()关闭连接** ```java // 方法一: socket.close(); // 方法二: // 给服务器发送相关消息,由服务器断开与指定客户端的连接 【备注】更多详情介绍见项目说明!
使用Python的套接字自动传输图片文件,你可以按照以下步骤进行操作: 1. 在发送端(客户端)将图片文件打开并读取为二进制数据。 2. 创建一个套接字并连接到接收端(服务器)的地址和端口。 3. 在发送端将图片数据通过套接字发送给接收端。 4. 在接收端接收数据并保存为图片文件。 以下是一个简单的示例代码,演示了如何使用socket套接字自动传输图片文件: **发送端(客户端)代码:** ```python import socket def send_image(image_path, host, port): # 打开并读取图片文件 with open(image_path, 'rb') as file: image_data = file.read() # 创建套接字并连接到服务器 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((host, port)) # 发送图片数据 client_socket.sendall(image_data) # 关闭套接字 client_socket.close() # 示例使用 image_path = 'image.jpg' # 图片文件路径 host = '服务器地址' port = 12345 # 服务器端口 send_image(image_path, host, port) ``` **接收端(服务器)代码:** ```python import socket def receive_image(save_path, host, port): # 创建套接字并绑定到指定地址和端口 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((host, port)) # 监听连接 server_socket.listen(1) print(f"等待来自客户端的连接...") # 接受连接并接收图片数据 client_socket, address = server_socket.accept() print(f"连接来自:{address}") # 接收图片数据 image_data = client_socket.recv(1024) # 保存图片文件 with open(save_path, 'wb') as file: file.write(image_data) # 关闭套接字 client_socket.close() server_socket.close() print("图片接收完成!") # 示例使用 save_path = 'received_image.jpg' # 接收到的图片保存路径 host = '0.0.0.0' # 服务器地址(监听所有可用的网络接口) port = 12345 # 服务器端口 receive_image(save_path, host, port) ``` 在上述代码中,你需要将`image.jpg`替换为要发送的图片文件路径。在接收端,你需要指定一个保存接收到的图片的路径`received_image.jpg`。此外,你还需要指定发送端和接收端所在的服务器地址和端口。 请注意,这只是一个简单的示例,仅用于演示基本的图片文件传输。在实际应用中,你可能需要添加错误处理、文件大小处理等逻辑。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值