关于Socket.Receive()与Send()的常见问题

Socket.Receive 方法 (Byte(), Int32, Int32, SocketFlags)

使用指定的 SocketFlags,从绑定的 Socket 接收指定的字节数,存入接收缓冲区的指定偏移量位置。

public int Receive(
	byte[] buffer,
	int offset,
	int size,
	SocketFlags socketFlags
)

参数
buffer
类型:System.Byte()
Byte 类型的数组,它是存储接收到的数据的位置。
offset
类型:System.Int32
buffer 中存储所接收数据的位置。
size
类型:System.Int32
要接收的字节数。
socketFlags
类型:System.Net.Sockets.SocketFlags
SocketFlags 值的按位组合。
返回值
类型:System.Int32
接收到的字节数。

(以上部分来自MSDN)
接下来我的经验总结:

1,Socket实际接收到的字节数与预期想要接收字节数

我们从上面的返回值可以清晰的知道,虽然我们给Buffer定义了一个长度和要接收到的size,但是Socket接收到的字节数(即返回值)并不一定等于Size

所以我们在做这个的时候,需要判断,实际接收到的字节数是否等于要接收的字节数!这个很重要。

 //定义接收数据的缓存
 byte[] body = new byte[1024];
 //第一次接收的实际数据 flag
 int flag = socket.Receive(body, 0, body.Length, SocketFlags.None);
 //如果没有接收到定长的数据,循环接收
 while(flag != body.Length)
 {
    flag += socket.Receive(body, flag, body.Length - flag, SocketFlags.None);
 }

因此,Socket接收数据的过程应该是这样的!

2,服务器发送多次,客户端一次接收的问题。

当然在实际的编程中很少这么干的,但是我这么写是想说明一个问题:假如客户端想一次接收服务器多次发送过来的数据,客户端定义缓存body的长度等于服务端多次发送的数据长度和,客户端在服务器发送完成后开始一次接收,客户端肯定能接收到服务器发送过来的所有数据,不会只接收服务器发送过来的部分数据。

我的叙述很烂!打个比方,假如服务器分三次分别发送“Hell”,“0”,“World”。这样客户端一定能接受到“HelloWorld”。而不是“Hell”或者其他什么的东西。

3,Socket.Send()

Send 将数据同步发送到 Connect 或 Accept 方法中指定的远程主机,并返回成功发送的字节数。Send 对面向连接的协议和无连接协议均适用。

此重载要求一个缓冲区来包含要发送的数据。SocketFlags 的默认值为 0,缓冲区偏移量的默认值为 0,发送字节数的默认值为缓冲区的大小。

如果您使用的是无连接协议,则必须先调用 Connect 才能调用此方法,否则 Send 将引发 SocketException。

如果您使用的面向连接的协议,则必须使用 Connect 建立远程主机连接,或者使用 Accept 接受传入的连接。

如果您使用的是无连接协议,并且打算将数据发送到若干不同的主机,则应使用 SendTo 方法。如果不使用 SendTo 方法,则每次调用 Send 之前必须调用 Connect。即使已经用 Connect 建立了默认远程主机,也可以使用 SendTo。通过另外调用 Connect,也可以在调用 Send 之前更改默认远程主机。

如果您使用的是面向连接的协议,则除非使用 Socket.SendTimeout 设置了超时值,否则,Send 将一直处于阻止状态,直到发送完缓冲区中的所有字节。如果超过超时值,Send 调用将引发 SocketException。在非阻止模式下,Send 可能会成功完成,即使它发送的字节数小于缓冲区中的字节数。应由您的应用程序负责跟踪已发送的字节数并重试操作,直到应用程序发送了缓冲区中的字节数为止。不能保证发送的数据会立即出现在网络上。为提高网络效率,基础系统可能会延迟传输,直到收集了足够多的传出数据后才开始发送。Send 方法的成功完成意味着基础系统有空间来缓冲用于网络发送的数据。

 4,总结

Send()方法只是将发送缓存的数据压入基础系统的控件中。而Receive()方法只是数据通过网络到达机器后,从基础系统的空间中读取数据。

 

  • 12
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
优化并改编以下代码,使其和原来有部分出入但实现效果相同: 1. import socket 2. 3. 4. def receive(): 5. # 创建套接字 6. udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7. 8. # 准备数据9. file_name = input("Please input the save file name:") 10. 11. # 发送数据 12. ip = input("Please input the sender's ipv4 address:") 13. udp_socket.sendto(file_name.encode('gbk'), (ip, 7788)) 14. 15. # 接收数据 16. recv_data = udp_socket.recvfrom(1024) 17. file_data = recv_data[0] 18. with open(file_name, 'wb') as f: 19. f.write(file_data) 20. print("Receive successfully!") 21. # 关闭套接字 22. udp_socket.close() 23. 24. 25.def send(): 26. # 创建套接字 27. udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 28. 29. # 绑定本地信息 30. localaddr = ('', 7788) 31. udp_socket.bind(localaddr) 32. 33. # 接收数据 34. while True: 35. recv_data = udp_socket.recvfrom(1024) 36. recv_msg = recv_data[0] 37. send_addr = recv_data[1] 38. print("%s:%s" % (str(send_addr), recv_msg.decode('gbk'))) 39. 40. # 读取文件并传输文件 41. with open(recv_msg.decode('gbk'), 'rb') as f: 42. file_data = f.read() 43. udp_socket.sendto(file_data, send_addr) 44. 45. print("Send successfully!") 46. break 47. 48. # 关闭套接字 49. udp_socket.close() 50. 51. 52.if name == 'main': 3553. while True: 54. answer = input("This is a simple program relying on the Udp protocol, \nif you want to send the file," 55. "please input 1,\n if you want to receive th e file, please input 2, \n if you want exit, " 56. "please input 0:\n") 57. if answer == '0': 58. break 59. if answer == '1': 60. send() 61. if answer == '2': 62. receive()
05-24
以下是优化并改编后的代码,改动主要是将 receive() 和 send() 函数中的 input() 改为函数参数接收: ``` import socket def receive(file_name, ip): # 创建套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 发送数据 udp_socket.sendto(file_name.encode('gbk'), (ip, 7788)) # 接收数据 recv_data = udp_socket.recvfrom(1024) file_data = recv_data[0] with open(file_name, 'wb') as f: f.write(file_data) print("Receive successfully!") # 关闭套接字 udp_socket.close() def send(file_path): # 创建套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定本地信息 localaddr = ('', 7788) udp_socket.bind(localaddr) # 接收数据 while True: recv_data = udp_socket.recvfrom(1024) recv_msg = recv_data[0] send_addr = recv_data[1] print("%s:%s" % (str(send_addr), recv_msg.decode('gbk'))) # 读取文件并传输文件 with open(file_path, 'rb') as f: file_data = f.read() udp_socket.sendto(file_data, send_addr) print("Send successfully!") break # 关闭套接字 udp_socket.close() if __name__ == '__main__': while True: answer = input("This is a simple program relying on the Udp protocol, \nif you want to send the file, please input 1,\nif you want to receive the file, please input 2, \nif you want exit, please input 0:\n") if answer == '0': break if answer == '1': file_path = input("Please input the file path to send:") send(file_path) if answer == '2': file_name = input("Please input the save file name:") ip = input("Please input the sender's ipv4 address:") receive(file_name, ip) ``` 改编后的代码中,receive() 和 send() 函数分别接收一个参数,即文件名或文件路径,而不是通过 input() 函数从命令行读取。这样可以使代码更加模块化和可重用。同时,代码中的注释也进行了修改和补充,以提高代码可读性和易懂性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值