套接字中的TCP协议

socket(套接字)

本地回环地址

127.0.0.1

我们先来写一个简单地服务器和客户端

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import socket
server = socket.socket()  # 就比如买了一个手机
server.bind(( "127.0.0.1" , 8080 ))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen( 5 # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept()  # 接听电话等着别人给你打电话
date = conn.recv( 1024 # 听别人说话,接收1023个字节数
print (date)
conn.send(b "hello" # 给别人回话
conn.close()  # 挂断电话
server.close()  # 关机

客户端

?
1
2
3
4
5
6
7
8
9
10
11
import socket
client = socket.socket()  #拿电话
client.connect(( "127.0.0.1" , 8080 ))  #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b "hello" # 对别人发消息
date = client.recv( 1024 #接收别人说话,没次接收1024个字节
print (date)
client.close()  # 挂电话

注意,在我们写服务器与客户端的时候

send与recv必须要一一对应

不能出现两边都相同的

recv是跟内存要数据,至于数据的来源你无需考虑

粘包

我们来看下面的的代码

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket
server = socket.socket()  # 就比如买了一个手机
server.bind(( "127.0.0.1" , 8088 ))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen( 5 # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept()  # 接听电话等着别人给你打电话
date = conn.recv( 1024 # 听别人说话,接收1023个字节数
print (date)
date = conn.recv( 1024 # 听别人说话,接收1023个字节数
print (date)
conn.close()  # 挂断电话
server.close()  # 关机

客户端

?
1
2
3
4
5
6
7
8
9
10
import socket
client = socket.socket()  #拿电话
client.connect(( "127.0.0.1" , 8088 ))  #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b "hello" # 对别人发消息
client.send(b "hello" # 对别人发消息
client.close()  # 挂电话

服务端打印结果

?
1
b 'hellohello'

这是应为;

tcp协议会将时间间隔短的,和文件大小小的会一次打包发个对方

如果我们将代码改一下,将服务端收到的字节数1024改为我们知道的字节数,看看是否还会粘包

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket
server = socket.socket()  # 就比如买了一个手机
server.bind(( "127.0.0.1" , 8088 ))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen( 5 # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept()  # 接听电话等着别人给你打电话
date = conn.recv( 5 # 听别人说话,接收1023个字节数
print (date)
date = conn.recv( 5 # 听别人说话,接收1023个字节数
print (date)
conn.close()  # 挂断电话
server.close()  # 关机

客户端

?
1
2
3
4
5
6
7
8
9
10
import socket
client = socket.socket()  #拿电话
client.connect(( "127.0.0.1" , 8088 ))  #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b "hello" # 对别人发消息
client.send(b "hello" # 对别人发消息
client.close()  # 挂电话

打印结果

?
1
2
b 'hello'
b 'hello'

在我们知道了,我么传输的文件大小是多大的时候,规定给接收数据的recv就会避免粘包

解决粘包问题

struct模块

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import struct
print ( "--------------------------1--------------------------" )
msg = "asdasdasdasdasd"
print ( "原字符串的长度" )
print ( len (msg))
handler = struct.pack( "i" , len (msg))
print ( "创建报头的长度" )
print ( len (handler))
res = struct.unpack( "i" ,handler)[ 0 ]
print ( "解报头过后的长度" )
print (res)
print ( "--------------------------2--------------------------" )
msg1 = "asdasdasdasdasdasdasdasd"
print ( "原字符串的长度" )
print ( len (msg1))
handler1 = struct.pack( "i" , len (msg1))
print ( "创建报头的长度" )
print ( len (handler1))
res1 = struct.unpack( "i" ,handler1)[ 0 ]
print ( "解报头过后的长度" )
print (res1)
"""
--------------------------1--------------------------
原字符串的长度
15
创建报头的长度
4
解报头过后的长度
15
--------------------------2--------------------------
原字符串的长度
24
创建报头的长度
4
解报头过后的长度
24
"""

经过观察这个模块会将自己的长度固定为4,8几个等级,我们一般选i就够用了

我们就可以将这个发过去,解报头,就可以知道原来的数据的字节大小

如果这个字节大小比我们接收的大

我们就然他一直接收,直达接收完为止

代码如下

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import socket
import subprocess
import json
import struct
# 创建套接字
server = socket.socket()
# 绑定端口号,及IP地址
server.bind(( "127.0.0.1" , 8181 ))
# 进入半连接池状态
server.listen( 5 )
while True :
     conn, address = server.accept()  # 等待客户端连接
     while True :
         # 判断是否会出错
         try :
             date = conn.recv( 1024 ).decode( "utf-8" # 接收客户端发来的数据
             if len (date) = = 0 : break  # 判断客户端发来的数据是否为空,为空就退出循环,关闭这个通道
             # 创建一个subprocess的对象
             obj = subprocess.Popen(date, shell = True , stdout = subprocess.PIPE, stderr = subprocess.PIPE)
             # 将对象拿到的类容做个合并
             res = obj.stdout.read() + obj.stderr.read()
             # 创建一个字典,将拿到的数据的字符数,装入字典中
             dic = { "file_size" : len (res)}
             # 将字典数据类型,转换为json的字符串数据类型
             json_dic = json.dumps(dic)
             # 将json字符串数据类型,创建为报头
             handler = struct.pack( "i" , len (json_dic))
             # 发送报头
             conn.send(handler)
             # 发送json字符串的字典
             conn.send(json_dic.encode( "utf-8" ))
             # 发送原始数据
             conn.send(res)
         # 捕捉异常
         except ConnectionResetError:
             break
     # 关闭通道
     conn.close()

客户端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import socket
import struct
import json
# 创键套接字
client = socket.socket()
# 绑定端口和IP地址
client.connect(( "127.0.0.1" , 8181 ))
while True :
     # 用户输入发送内容,并装换成2进制
     msg = input ( "cmd>>>" ).strip().encode( "utf-8" )
     # 判断用户输入是否为空,为空跳过本次循环
     if len (msg) = = 0 : continue
     # 发送数据
     client.send(msg)
     # 接收报头
     handler = client.recv( 4 )
     # 解析报头,注意必须加索引,拿到json过后的字典的字节数
     json_dic_size = struct.unpack( "i" , handler)[ 0 ]
     # 接收json字典的字节数的json字典
     json_dic = client.recv(json_dic_size)
     # 将json数据的字典,反序列化
     dic = json.loads(json_dic)
     # 定义一个字节数为0 的变量
     msg_size = 0
     # 定义一个空的2进制字符
     msg_date = b''
     # 判断,如果定义的字节数小于我们字典中的字节数就读取文件,如果等于和大于就退出循环,打印文件类容
     while msg_size < dic.get( "file_size" ):
         date = client.recv( 1024 )
         # 文件类容累加
         msg_date + = date
         # 字节数累加
         msg_size + = len (date)
     print (msg_date.decode( "gbk" ))

  


 

socket(套接字)

本地回环地址

127.0.0.1

我们先来写一个简单地服务器和客户端

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import socket
server = socket.socket()  # 就比如买了一个手机
server.bind(( "127.0.0.1" , 8080 ))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen( 5 # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept()  # 接听电话等着别人给你打电话
date = conn.recv( 1024 # 听别人说话,接收1023个字节数
print (date)
conn.send(b "hello" # 给别人回话
conn.close()  # 挂断电话
server.close()  # 关机

客户端

?
1
2
3
4
5
6
7
8
9
10
11
import socket
client = socket.socket()  #拿电话
client.connect(( "127.0.0.1" , 8080 ))  #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b "hello" # 对别人发消息
date = client.recv( 1024 #接收别人说话,没次接收1024个字节
print (date)
client.close()  # 挂电话

注意,在我们写服务器与客户端的时候

send与recv必须要一一对应

不能出现两边都相同的

recv是跟内存要数据,至于数据的来源你无需考虑

粘包

我们来看下面的的代码

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket
server = socket.socket()  # 就比如买了一个手机
server.bind(( "127.0.0.1" , 8088 ))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen( 5 # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept()  # 接听电话等着别人给你打电话
date = conn.recv( 1024 # 听别人说话,接收1023个字节数
print (date)
date = conn.recv( 1024 # 听别人说话,接收1023个字节数
print (date)
conn.close()  # 挂断电话
server.close()  # 关机

客户端

?
1
2
3
4
5
6
7
8
9
10
import socket
client = socket.socket()  #拿电话
client.connect(( "127.0.0.1" , 8088 ))  #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b "hello" # 对别人发消息
client.send(b "hello" # 对别人发消息
client.close()  # 挂电话

服务端打印结果

?
1
b 'hellohello'

这是应为;

tcp协议会将时间间隔短的,和文件大小小的会一次打包发个对方

如果我们将代码改一下,将服务端收到的字节数1024改为我们知道的字节数,看看是否还会粘包

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import socket
server = socket.socket()  # 就比如买了一个手机
server.bind(( "127.0.0.1" , 8088 ))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen( 5 # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept()  # 接听电话等着别人给你打电话
date = conn.recv( 5 # 听别人说话,接收1023个字节数
print (date)
date = conn.recv( 5 # 听别人说话,接收1023个字节数
print (date)
conn.close()  # 挂断电话
server.close()  # 关机

客户端

?
1
2
3
4
5
6
7
8
9
10
import socket
client = socket.socket()  #拿电话
client.connect(( "127.0.0.1" , 8088 ))  #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b "hello" # 对别人发消息
client.send(b "hello" # 对别人发消息
client.close()  # 挂电话

打印结果

?
1
2
b 'hello'
b 'hello'

在我们知道了,我么传输的文件大小是多大的时候,规定给接收数据的recv就会避免粘包

解决粘包问题

struct模块

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import struct
print ( "--------------------------1--------------------------" )
msg = "asdasdasdasdasd"
print ( "原字符串的长度" )
print ( len (msg))
handler = struct.pack( "i" , len (msg))
print ( "创建报头的长度" )
print ( len (handler))
res = struct.unpack( "i" ,handler)[ 0 ]
print ( "解报头过后的长度" )
print (res)
print ( "--------------------------2--------------------------" )
msg1 = "asdasdasdasdasdasdasdasd"
print ( "原字符串的长度" )
print ( len (msg1))
handler1 = struct.pack( "i" , len (msg1))
print ( "创建报头的长度" )
print ( len (handler1))
res1 = struct.unpack( "i" ,handler1)[ 0 ]
print ( "解报头过后的长度" )
print (res1)
"""
--------------------------1--------------------------
原字符串的长度
15
创建报头的长度
4
解报头过后的长度
15
--------------------------2--------------------------
原字符串的长度
24
创建报头的长度
4
解报头过后的长度
24
"""

经过观察这个模块会将自己的长度固定为4,8几个等级,我们一般选i就够用了

我们就可以将这个发过去,解报头,就可以知道原来的数据的字节大小

如果这个字节大小比我们接收的大

我们就然他一直接收,直达接收完为止

代码如下

服务端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import socket
import subprocess
import json
import struct
# 创建套接字
server = socket.socket()
# 绑定端口号,及IP地址
server.bind(( "127.0.0.1" , 8181 ))
# 进入半连接池状态
server.listen( 5 )
while True :
     conn, address = server.accept()  # 等待客户端连接
     while True :
         # 判断是否会出错
         try :
             date = conn.recv( 1024 ).decode( "utf-8" # 接收客户端发来的数据
             if len (date) = = 0 : break  # 判断客户端发来的数据是否为空,为空就退出循环,关闭这个通道
             # 创建一个subprocess的对象
             obj = subprocess.Popen(date, shell = True , stdout = subprocess.PIPE, stderr = subprocess.PIPE)
             # 将对象拿到的类容做个合并
             res = obj.stdout.read() + obj.stderr.read()
             # 创建一个字典,将拿到的数据的字符数,装入字典中
             dic = { "file_size" : len (res)}
             # 将字典数据类型,转换为json的字符串数据类型
             json_dic = json.dumps(dic)
             # 将json字符串数据类型,创建为报头
             handler = struct.pack( "i" , len (json_dic))
             # 发送报头
             conn.send(handler)
             # 发送json字符串的字典
             conn.send(json_dic.encode( "utf-8" ))
             # 发送原始数据
             conn.send(res)
         # 捕捉异常
         except ConnectionResetError:
             break
     # 关闭通道
     conn.close()

客户端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import socket
import struct
import json
# 创键套接字
client = socket.socket()
# 绑定端口和IP地址
client.connect(( "127.0.0.1" , 8181 ))
while True :
     # 用户输入发送内容,并装换成2进制
     msg = input ( "cmd>>>" ).strip().encode( "utf-8" )
     # 判断用户输入是否为空,为空跳过本次循环
     if len (msg) = = 0 : continue
     # 发送数据
     client.send(msg)
     # 接收报头
     handler = client.recv( 4 )
     # 解析报头,注意必须加索引,拿到json过后的字典的字节数
     json_dic_size = struct.unpack( "i" , handler)[ 0 ]
     # 接收json字典的字节数的json字典
     json_dic = client.recv(json_dic_size)
     # 将json数据的字典,反序列化
     dic = json.loads(json_dic)
     # 定义一个字节数为0 的变量
     msg_size = 0
     # 定义一个空的2进制字符
     msg_date = b''
     # 判断,如果定义的字节数小于我们字典中的字节数就读取文件,如果等于和大于就退出循环,打印文件类容
     while msg_size < dic.get( "file_size" ):
         date = client.recv( 1024 )
         # 文件类容累加
         msg_date + = date
         # 字节数累加
         msg_size + = len (date)
     print (msg_date.decode( "gbk" ))

  


 

猜你喜欢

转载自www.cnblogs.com/le-le666/p/11324291.html