socket进阶

1、关于send和sendall的区别

sendall:
Send a data string to the socket.  For the optional flags
argument, see the Unix manual. This calls send() repeatedly
until all data is sent. If an error occurs, it's impossible
to tell how much data has been sent.
send:
Send a data string to the socket.  For the optional flags
argument, see the Unix manual. Return the number of bytes
sent; this may be less than len(data) if the network is busy.
有上面两段话可知,send发送数据(数据需为bytes)可能会小于要发送的数据长度,返回的是已经发送的字节数,sendall就是循环调用send直到数据全部发送完成,所以下面两段代码是等价的
client = socket.socket()       #创建一个套接字实例
client.connect(addr)           #将套接字连接到远程地址

client.sendall(b'hello,world')     #发送数据,只能是bytes形式,等同于下面这段

# buffer = b'hello,world'
# while buffer:
#     b = client.send(buffer)
#     buffer = buffer[b:]

2、粘包问题

先看段代码

 1 import socket
 2 import time
 3 addr = ("localhost",1024)
 4 
 5 server = socket.socket()  #创建一个socket实例
 6 
 7 server.bind(addr)  #绑定套接字绑定到本地地址,格式为(地址,端口)
 8 server.listen(5)    #监听端口
 9 
10 conn,addr = server.accept() #等待请求
11 print(conn,addr)#conn就是客户端连接过来而在服务器端为其生成的一个实例,addr客户端地址,格式为(地址,端口)
12 
13 
14 
15 data1 = conn.recv(1024)  #接收数据
16 data2 = conn.recv(1024)
17 print("data1:{},data2:{}".format(data1,data2))
18 
19 server.close()  #关闭连接
 1 import socket
 2 
 3 addr = ("localhost",1024)
 4 
 5 client = socket.socket()       #创建一个套接字实例
 6 client.connect(addr)           #将套接字连接到远程地址
 7 
 8 client.send(b'hello')     #发送数据,只能是bytes形式
 9 client.send(b'world')
10 
11 client.close()                         #关闭连接

这里客户端连续发送两次,服务器端接收两次,我们想要的结果是,依次收到hello和world,但是结果是这样子的:

<socket.socket fd=420, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 1024), raddr=('127.0.0.1', 51569)> ('127.0.0.1', 51569)
data1:b'helloworld',data2:b''

连在一起发送过来了

所谓粘包问题主要还是C/S两端数据传输时  因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
根本原因:
粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。udp并不会出现该问题
解决方法:
1、自定义字典类型 的数据报头{文件名:a,文件的size:1090}计算出该报头的长度(len(字节))
2、使用struct.pack('i',报头长度(一个数字))把一个数字压缩成固定的size 4个字节,发送给对端。

3、对端 struct.unpack(‘i’,recv(4))接收固定大小4个字节;这就是接收到了 报头的长度。

4.recv(报头长度)这就是发送过来的报头信息了
对上面例子进行优化:
#client

import socket
import struct
addr = ("localhost",1024)

client = socket.socket()       #创建一个套接字实例
client.connect(addr)           #将套接字连接到远程地址

msg = b'hello'
msg1 = b'world'
msg_size = len(msg)


a = struct.pack('i',msg_size)
client.send(a)             #发送数据长度
client.send(b'hello')     #这里我们只要处理这个粘包就可以了
client.send(msg1)

client.close()                         #关闭连接


#server
import socket
import time
addr = ("localhost",1024)

server = socket.socket()  #创建一个socket实例

server.bind(addr)  #绑定套接字绑定到本地地址,格式为(地址,端口)
server.listen(5)    #监听端口

conn,addr = server.accept() #等待请求
print(conn,addr)#conn就是客户端连接过来而在服务器端为其生成的一个实例,addr客户端地址,格式为(地址,端口)


size = conn.recv(4)   #接收报头,4bytes
print(size[0])
data1 = conn.recv(size[0])  #size[0]就是要接收的数据长度
data2 = conn.recv(1024)
print("data1:{},data2:{}".format(data1,data2))

server.close()  #关闭连接

  

 


posted @ 2017-07-03 21:06  zj-luxj  阅读(188)  评论(0编辑  收藏  举报