网络编程之Tcp,udp

1.TCP

2.补充异常处理

3.UDP

一TCP.
1.socket基础版本通讯

服务端:

 1 import socket
 2 
 3 server = socket.socket()# 生成一个对象
 4 server.bind(('127.0.0.1',8080))# 绑定ip和端口
 5 server.listen(5)# 连接池
 6 
 7 conn,addr = server.accept()# 等待别人 一个是ip 一个是地址  阻塞
 8 data = conn.recv(1024) # 听别人说话   阻塞
 9 print(data)
10 conn.send(b'hello boby')# 给对方回信息
11 
12 conn.close()# 断开链接
13 server.close()# 结束掉进程
View Code

客户端:

 1 import socket
 2 
 3 
 4 client = socket.socket()# 生成一个对象
 5 
 6 client.connect(('127.0.0.1',8080))# 发送你的ip和端口号
 7 
 8 client.send(b'hello word')# 对别人说话
 9 data = client.recv(1024) # 听别人说话
10 print(data)
11 
12 client.close()# 断开链接
View Code

2 升级版实现多条通讯

服务端:

 1 import socket
 2 
 3 server = socket.socket() # 创建一个对象
 4 server.bind(('127.0.0.1',8989)) # 绑定 ip 和端口
 5 server.listen(5)# 最大连接数
 6 
 7 while True:
 8     conn,addr = server.accept() # 等待 别人链接进来  ip和端口 # 等到别人来  conn就类似于是双向通道
 9     print(conn)
10     print(addr)## ('127.0.0.1', 51323) 客户端的地址
11     while True:
12         try:
13             data = conn.recv(1024)# 接收数据
14             # 判断接收的数据为不为空
15             if not data:break## b''  针对mac与linux 客户端异常退出之后 服务端不会报错 只会一直收b''
16             conn.send(data.upper())
17         except ConnectionResetError as e:
18             print(e)
19             break
20     conn.close()
View Code

客户端:

 1 import socket
 2 
 3 client = socket.socket() # 创建一个对象
 4 client.connect(('127.0.0.1',8989))# 写入要链接的ip 和端口号
 5 
 6 while True:
 7     msg = input('请输入:>>>').encode('utf-8')# 发送数据
 8     # 判断数据是否为空
 9     if not msg:continue
10     client.send(msg)
11     # 接收数据
12     data = client.recv(1024)
13     print(data)
View Code

tcp的沾包问题

客户端:

import time
import socket

client = socket.socket()
client.connect(('127.0.0.1',8080))


client.send(b'hello')
# time.sleep(5)
client.send(b'word')

服务端:

import socket

server =  socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
conn,addr = server.accept()

data = conn.recv(5)
print(data)
data = conn.recv(5)
print(data)

'''
可以规定接收的数据长度,但是你不知道对方每次要发多少数据,这个接收数据的1024不能随便改,所以说要想其他的方法
解决方案就是你要提前告知收方数据的长度
'''

那么沾包是如何产生的:

TCP流式协议, 数据之间没有分界, 就像水  一杯水和一杯牛奶倒在一起了!

UDP 用户数据报协议    

粘包 仅发生在TCP协议中   

1. 发送端 发送的数据量小 并且间隔短 会粘
2. 接收端 一次性读取了两次数据的内容    会粘 
3. 接收端 没有接收完整  剩余的内容 和下次发送的粘在一起

无论是那种情况,其根本原因在于  接收端不知道数据到底有多少 

解决方案就是 提前告知接收方 数据的长度  

那么如何解决沾包:

.TCP粘包问题
struct模块 对数据进行打包处理 固定长度
pack
unpack

服务端
1.生成一个字典
2.制作该字典的报头
json序列化
编码 统计长度
3.发送字典的报头
4.发送字典
5.最后发真实数据

客户端
1.先接受固定长度的4个字节字典报头
2.解析获取字典数据的长度
unpack(...)[0]
3.接受字典数据
解码 反序列化
4.接受真实数据

ps:为什么要多加一个字典
1.打包的数据大小有限
2.可以携带更多的信息

代码实现解决沾包问题:
客户端:

 1 import socket
 2 import struct
 3 import json
 4 
 5 client = socket.socket()
 6 client.connect(('127.0.0.1',8080))
 7 
 8 while True:
 9     msg = input('msg:>>>').encode('utf-8')
10     if not msg:continue
11     client.send(msg)
12     # 接收字典报头
13     header = client.recv(4)
14     # 解析报头,拿到真实的数据长度
15     header_dict = struct.unpack('i',header)[0]
16     # 接收字典数据
17     dict_bytes = client.recv(header_dict)
18     json_dict = json.loads(dict_bytes.decode('utf-8'))
19     # 从字典中获取信息
20     print(json_dict)
21     recv_size = 0
22     real_data = b''
23     # 循环接收字典中的信息
24     while recv_size < json_dict.get('file_size'):
25         data = client.recv(1024)
26         recv_size +=len(data)
27         real_data +=data
28         print(data.decode('gbk'))
29         print(recv_size)
View Code

服务端:

 1 import socket
 2 import struct
 3 import json
 4 import subprocess
 5 
 6 server = socket.socket()
 7 server.bind(('127.0.0.1',8080))
 8 server.listen(5)
 9 
10 while True:
11     conn,addr = server.accept()
12     while True:
13         try:
14             cmd = conn.recv(1024)
15             if not cmd:break
16             cmd = cmd.decode('utf-8')
17             obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
18             res = obj.stdout.read()+obj.stderr.read()
19             # 定义一个字典
20             d = {'name':'zy','file_size':len(res)}
21             json_d = json.dumps(d)
22             #1.制作字典报头
23             header = struct.pack('i',len(json_d))
24             #2. 发送字典报头
25             conn.send(header)
26             #3.发送字典
27             conn.send(json_d.encode('utf-8'))
28             #4.发送正式数据
29             conn.send(res)
30         except ConnectionResetError as e:
31             print(e)
32             break
33     conn.close()
View Code

补充:

解决沾包问题需要用到的struct模块:

 1 import struct
 2 #
 3 # res = 'dgbdushfgcbdsjhgbcshjdgcbidjhgvKADJFCNalskCNkfjcnLJCHSDNCKLSD'
 4 # print('最开始的',len(res))
 5 #
 6 # res1 = struct.pack('i',len(res))
 7 # print('打包好的',len(res1))
 8 #
 9 # res2 = struct.unpack('i',res1)[0] # 如果不加索引解包后的结果就是解包后的 (61,),索引拿到的是0号位
10 # print('解包后的',res2)
11 
12 
13 
14 '''
15  当原始数据特别大的时候 i模式打包不了 需要更换模式?
16  如果遇到数据量特别大的情况 该如何解决?
17  可以定义一个字典
18 '''
19 d = {
20     'name':'zy',
21     'file_size':'123473985961732617417324627346723641746723462374613274164',
22     'info':'你好',
23 }
24 
25 import json
26 json_d = json.dumps(d)
27 print(len(json_d))
28 
29 res1 = struct.pack('i',len(json_d))
30 print(len(res1))
31 
32 res2 = struct.unpack('i',res1)[0]
33 print('解包后的数据',res2)
View Code

补充:复习subprocess模块

 1 import subprocess
 2 
 3 while True:
 4     cmd = input('cmg:>>>')
 5     if cmd == 'q':break
 6     if not cmd:continue
 7     obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
 8     print(obj.stdout.read().decode('gbk'))# 正确命令返回的结果
 9     print(obj.stderr.read().decode('gbk'))# 错误的命令返回的结果
10 
11 #注意:subprocess获取到的数据,拿完就没有了,不能重复的拿
View Code

练习:大文件上传:

客户端:

 1 mport struct
 2 import json
 3 import socket
 4 import os
 5 
 6 client = socket.socket()
 7 client.connect(('127.0.0.1',8080))
 8 
 9 while True:
10     # 获取文件路径
11     MOVIE_DIR = r'H:python视频python10期day29视频'
12     # 显示文件里的电影
13     movie_list = os.listdir(MOVIE_DIR)
14     # 循环打印电影
15     for i,movie in enumerate(movie_list,1):
16         print(i,movie)
17     # 选取电影
18     choice = input('请选择电影:>>>').strip()
19     if choice.isdigit():
20         choice = int(choice)-1
21         # 判断是否在索引范围内
22         if choice in range(0,len(movie_list)):
23             # 获取要上传电影的文件路径
24             path = movie_list[choice]
25             # 获取要上传电影的绝对路径,拼接
26             file_path = os.path.join(MOVIE_DIR,path)
27             # 获取文件的大小
28             file_size = os.path.getsize(file_path)
29             # 定义一个字典
30             d = {
31                 'file_name':'qwer.mp4',
32                 'file_size':file_size,
33             }
34             # 序列化字典
35             json_d = json.dumps(d)
36             json_bytes = json_d.encode('utf-8')
37             # 制作字典报头
38             header = struct.pack('i',len(json_bytes))
39             # 发送字典报头
40             client.send(header)
41             # 发送字典数据
42             client.send(json_bytes)
43             # 循环发送文件数据
44             with open(file_path,'rb') as f:
45                 for line in f:
46                     client.send(line)
47         else:
48             print('序号不在索引范围内')
49     else:
50         print('请输入数字')
View Code

服务端:

 1 import socket
 2 import struct
 3 import json
 4 
 5 
 6 server = socket.socket()
 7 server.bind(('127.0.0.1',8080))
 8 server.listen(5)
 9 
10 while True:
11     conn,addr = server.accept()
12     while True:
13         try:
14             # 先接收报头
15             header = conn.recv(4)
16             # 解析字典报头
17             header_len =struct.unpack('i',header)[0]
18             # 接收字典数据
19             json_bytes = conn.recv(header_len)
20             json_dict = json.loads(json_bytes.decode('utf-8'))
21             # 获取字典长度
22             len_size = json_dict.get('file_size')
23             # 循环接收文件数据
24             recv_size = 0
25             with open(json_dict.get('file_name'),'wb') as f:
26                 while recv_size < len_size:
27                     data = conn.recv(1024)
28                     f.write(data)
29                     recv_size+=len(data)
30                 print('上传成功')
31                 print(recv_size)
32         except ConnectionResetError as e:
33             print(e)
34             break
35     conn.close()
View Code

二.异常处理

什么是异常处理:

  程序在运行过程中出现了不可预知的错误,并且该程序没有对应的处理机制’,

  那么就会以异常的形式表示出来,造成的影响就是整个程序无法在正常运行了,

  那么我们一般不建议使用异常处理,一般的异常都是可以通过修改代码可以防止

  出现异常的,只有那么你知道会报错,而不能解决的,

  举例:比如说你和别人在打电话,那么可能会出现一些意外,可能手机关机了,或者没信号了

  所以说在无法解决的时候在使用异常处理

异常的结构:

  1.异常的类型:NAMEERROR

  2.异常的信息:name 'fdsdfsdf' is not defined

  3.异常的位置:Traceback (most recent call last):
  File "D:/python脱产10期视频/day29/01 异常处理.py", line 1, in <module>
  fdsdfsdf

异常的种类:

  分为俩大类:

    1.语法上的错误:

      是你程序启动立刻就能解决的,这种错误也是不能被容忍的,

      语法上的错误,发现之后应该立即解决

    2.逻辑上的错误:

      这种错误是可以被容忍的,因为一眼看不出来,

      针对逻辑上的错误,可以采用异常处理机制进行捕获

    

    常见的错误类型:

      NAMERROR 名字错误

      SyntaxError 语法错误
      eyError 键不存在
      ValueError 值错误
      IndexError 索引错误

  如何避免:

    异常处理:

      在你认为可能会出现的bug的代码上方try一下:注意try内部的代码块越少越好

      try:

        可能出现的错误

      except 出错的类型 as e: 将报错信息赋值给变量

        出错之后的处理机制

三.UDP协议

udp协议简介

用户数据报协议,是OSI模型中属于传输层的协议

提供,不可靠的,不要求顺序的,数据量小的,速度快的传输服务

不可靠:

发送完成后不需要确认信息 并且立即删除缓存中的数据

不要求顺序:

当一个数据较大时 会分为多个数据报来传输,对方无法获知数据的顺序,以及是否完整

数据量较小的:

数据越大丢包的可能性越高 ,建议的数据量不要超过1472

速度快:

相对于TCP而言快很多 不需要确认信息 ,也不需要建立链接

udp协议的通讯流程:

如果TCP比喻为手机打电话的过程 那么UDP可以看做是对讲机

1.买机器 创建UDP的socket

2.固定频道 bind一个ip和端口

3.收发数据 recvfrom sendto

 

接收

1.买机器 创建UDP的socket

2.收发数据 recvfrom sendto

注意 不能先收 要收数据 必须明确端口号 没有端口号是不可能使用网络服务的

 

 

TCP 与 UDP 的其他区别

1.没有连接

2.不会粘包 每次发送都是一个独立的数据包

 

TCP 对数据完整性要求较高 : 在线支付 ,文字信息

UDP: 对数据不要求完整性 但是要快 : 视频 语音 游戏

udp协议的基本使用:

服务端:

 1 import socket
 2 
 3 server = socket.socket(type=socket.SOCK_DGRAM)# UDP协议
 4 server.bind(('127.0.0.1',8080))
 5 '''
 6 UDP不需要设置半链接池,它也没有半连接池的概念
 7 因为没有双向通道,不需要accept 直接就是通信循环
 8 '''
 9 while True:
10     data,addr = server.recvfrom(1024)
11     print('数据',data)# 客户端发来的信息
12     print('地址',addr)# 客户端发来的地址
13     server.sendto(data.upper(),addr)
14     break
View Code

客户端:

 1 import socket
 2 
 3 client = socket.socket(type=socket.SOCK_DGRAM)
 4 #不需要建立链接,直接进入通信循环
 5 server_address = ('127.0.0.1',8080)
 6 
 7 while True:
 8     client.sendto(b'hello',server_address)
 9     data,addr = client.recvfrom(1024)
10     print('服务端发来的数据',data)
11     print('服务端发来的地址',addr)
12     break
View Code

udp通讯是不会沾包的:

服务端:

 1 import socket
 2 
 3 server = socket.socket(type=socket.SOCK_DGRAM)
 4 server.bind(('127.0.0.1',8080))
 5 
 6 data,addr = server.recvfrom(1024)
 7 print(data)
 8 data,addr1 = server.recvfrom(1024)
 9 print(data)
10 data,addr2 = server.recvfrom(1024)
11 print(data)
View Code

客户端:

1 import socket
2 
3 
4 client = socket.socket(type=socket.SOCK_DGRAM)
5 server_address = ('127.0.0.1',8080)
6 
7 client.sendto(b'hello',server_address)
8 client.sendto(b'hello',server_address)
9 client.sendto(b'hello',server_address)
View Code

小练习:udp实现简易版本的qq

服务端:

 1 import socket
 2 
 3 server = socket.socket(type=socket.SOCK_DGRAM)
 4 server.bind(('127.0.0.1',8080))
 5 
 6 
 7 while True:
 8     data,addr = server.recvfrom(1024)
 9     print(data.decode('utf-8'))
10     msg = input('msg:>>>').strip()
11     server.sendto(msg.encode('utf-8'),addr)
View Code

客户端1:

 1 import socket
 2 
 3 client = socket.socket(type=socket.SOCK_DGRAM)
 4 server_address = ('127.0.0.1',8080)
 5 
 6 while True:
 7     msg = input('msg:>>>').strip()
 8     msg = '来自客户端1的信息:%s'%msg
 9     client.sendto(msg.encode('utf-8'),server_address)
10     data,server_addr = client.recvfrom(1024)
11     print(data.decode('utf-8'))
View Code

客户端2:

 1 import socket
 2 
 3 client = socket.socket(type=socket.SOCK_DGRAM)
 4 server_address = ('127.0.0.1',8080)
 5 
 6 while True:
 7     msg = input('msg:>>>').strip()
 8     msg = '来自客户端2的信息:%s'%msg
 9     client.sendto(msg.encode('utf-8'),server_address)
10     data,server_addr = client.recvfrom(1024)
11     print(data.decode('utf-8'))
View Code

客户端3:

 1 import socket
 2 
 3 client = socket.socket(type=socket.SOCK_DGRAM)
 4 server_address = ('127.0.0.1',8080)
 5 
 6 while True:
 7     msg = input('msg:>>>').strip()
 8     msg = '来自客户端3的信息:%s'%msg
 9     client.sendto(msg.encode('utf-8'),server_address)
10     data,server_addr = client.recvfrom(1024)
11     print(data.decode('utf-8'))
View Code
原文地址:https://www.cnblogs.com/zahngyu/p/11324021.html