加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

day30:TCP&UDP:socket

发布时间:2020-12-20 09:56:25 所属栏目:Python 来源:网络整理
导读:目录 1.TCP协议和UDP协议 2.什么是socket? 3.socket正文 1.TCP基本语法 2.TCP循环发消息 3.UDP基本语法 4.UDP循环发消息 4.黏包 5.解决黏包问题 1.解决黏包方式一:先发送接下来要发送数据的大小 2.解决黏包方式二:conn.send("00000100".encode()) 3.前戏:str

目录

1.TCP协议和UDP协议

2.什么是socket?

3.socket正文

  1.TCP基本语法

  2.TCP循环发消息

  3.UDP基本语法

  4.UDP循环发消息

4.黏包

5.解决黏包问题

  1.解决黏包方式一:先发送接下来要发送数据的大小

  2.解决黏包方式二:conn.send("00000100".encode())

  3.前戏:struct模块

  4.解决黏包方式三:使用struct模块

1.TCP协议和UDP协议

TCP(Transmission Control Protocol)一种面向连接的、可靠的、传输层通信协议(比如:打电话)

优点:可靠,稳定,传输完整稳定,不限制数据大小

缺点:慢,效率低,占用系统资源高,一发一收都需要对方确认

应用:Web浏览器,电子邮件,文件传输,1)">大量数据传输的场景

UDP(User Datagram Protocol)一种无连接的,1)">不可靠的传输层通信协议(比如:发短信)

优点:速度快,可以多人同时聊天,1)">耗费资源少,不需要建立连接

缺点:不稳定,不能保证每次数据都能接收到

应用:IP电话,实时视频会议,聊天软件,1)">少量数据传输的场景

2.什么是socket?

socket的意义:通络通信过程中,信息拼接的工具(中文:套接字)

?

3.socket正文

1.TCP基本语法

服务端

#  ### 服务端
import socket
 1.创建一个socket对象
sk = socket.socket()

 2.绑定对应的ip和端口号(让其他主机在网络中可以找得到)
"""127.0.0.1代表本地ip"""
sk.bind( ("127.0.0.1",9001) )

 3.开启监听
sk.listen()

 4.建立三次握手
conn,addr  = sk.accept()

 5.处理收发数据的逻辑
recv 接受 send 发送
res = conn.recv(1024) 最多一次接受 1024 字节
print(res.decode(utf-8"))

 6.四次挥手
conn.close()

 7.退还端口
sk.close()

客户端

 ### 客户端
 2.与服务器建立连接
sk.connect( ( 3.发送数据(只能发送二进制的字节流)
sk.send(北京昨天迎来了暴雨,如今有车是不行的,还得有船".encode( 4.关闭连接
sk.close()

2.TCP循环发消息

服务端

 ### 服务端
 socket 
 1.创建socket对象
sk = socket.socket()
 # 在bind方法之前加上这句话,可以让一个端口重复使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

 2.绑定ip和端口号(在网络中注册该主机)
sk.bind( () )
sk.listen()

print(conn)
print(addr)
conn:<socket.socket fd=4,family=AddressFamily.AF_INET,type=SocketKind.SOCK_STREAM,proto=0,laddr=('127.0.0.1',9002),raddr=('127.0.0.1',53620)>
addr:('127.0.0.1',53620)
"""

 5.处理收发数据的逻辑 
send(字节流)"""

conn.send("我去北京先买船".encode("utf-8"))
"""
while True:
     4.三次握手
    conn,addr = sk.accept()
     True:
        res = conn.recv(1024)
        print(res.decode())
        strvar = input(请输入服务端要给客户端发送的内容)
        conn.send(strvar.encode())
        if strvar.upper() == Q:
            break

 2.连接服务端
sk.connect( ( 3.收发数据

res = sk.recv(1024) # 一次最多接受1024个字节
print(res.decode())
 True:
    strvar = input(请输入您要发送的内容:)
    sk.send(strvar.encode())
    res = sk.recv(1024)
    if res == bq" or res == b:
        break
    (res.decode())

 4.关闭连接
sk.close()

3.UDP基本语法

服务端

 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
 2.绑定地址端口号
sk.bind( ( 3.udp服务器,在一开始只能够接受数据
msg,cli_addr = sk.recvfrom(1024(msg.decode())
(cli_addr)

 服务端给客户端发送数据
msg = 我是你老娘,赶紧给我回家吃饭
sk.sendto(msg.encode(),cli_addr)

 4.关闭连接
sk.close()

客户端

socket.SOCK_DGRAM)

 2.收发数据的逻辑

 发送数据
msg = 你好,你是mm还是gg"
 sendto( 消息,(ip,端口号) )
sk.sendto( msg.encode(),()  )

 接受数据
msg,server_addr = sk.recvfrom(1024)
(server_addr)

 3.关闭连接
sk.close()

4.UDP循环发消息

服务端

 接受消息
    msg,1)">(msg.decode())
    message = input(服务端给客户端发送的消息是?: 发送数据
    sk.sendto(message.encode(),cli_addr)


 2.收发数据的逻辑
 发送数据
    message = input(客户端给服务端发送的消息是?:)
    sk.sendto(message.encode(),() )
    
     接受数据
    msg,addr = sk.recvfrom(1024print(msg.decode())
    

 3.关闭连接
sk.close()

4.黏包

1.出现黏包的原因

tcp协议在发送数据时,会出现黏包现象.

1.数据粘包是因为在客户端/服务器端都会有一个数据缓冲区,

缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区都会设置的比较大。

2.在收发数据频繁时,由于tcp传输消息的无边界,1)">不清楚应该截取多少长度

导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包

2.黏包出现的两种情况

黏包现象一:

在发送端,由于两个数据短,发送的时间隔较短,所以在发送端形成黏包

黏包现象二:

在接收端,由于两个数据几乎同时被发送到对方的缓存中,所有在接收端形成了黏包

总结:

发送端,包之间时间间隔短 或者 接收端,1)">接受不及时,就会黏包

核心是因为tcp对数据无边界截取,不会按照发送的顺序判断

3.黏包的应用场景

解决黏包场景:

应用场景在实时通讯时,需要阅读此次发的消息是什么

不需要解决黏包场景:

下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓.

5.解决黏包问题

1.解决黏包方式一:先发送接下来要发送数据的大小

服务端

 time
 socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,1)
sk.bind( () )
sk.listen()

conn,addr = sk.accept()


 处理收发数据的逻辑

 先发送接下来要发送数据的大小
conn.send(5".encode())  发送5个字节
# 发完长度之后,再发数据
conn.send(hello.encode())
conn.send(.encode())

conn.close()
sk.close()

客户端

 ### 客户端

黏包出现的两种情况:
    (1) 发送端发送数据太快
    (2) 接收端接收数据太慢
 time
sk = socket.socket()
sk.connect( () )

time.sleep(2)  睡2s,让其接受速度慢一些,制造黏包效果 处理收发数据的逻辑 先接受接下来要发送数据的大小
res = sk.recv(1)  res = "5"
num = int(res.decode())  num = 5 接受num这么多个字节数
res1 = sk.recv(num)  一次最多只能接收5个字节
res2 = sk.recv(1024(res1)
(res2)


sk.close()

2.解决黏包方式二:conn.send("00000100".encode())

服务端:

00000100.encode())
" * 20
conn.send(msg.encode())
conn.send(.encode())

conn.close()
sk.close()

客户端:

) )

time.sleep(2 先接受接下来要发送数据的大小
res = sk.recv(8)
num = int(res.decode())
 接受num这么多个字节数
res1 = sk.recv(num)
res2 = sk.recv(1024(res2)


sk.close()

其实,这两种写法都存在一定的限制,并非最完美的解决方案

下面介绍一个模块,用来完美的解决黏包现象

3.前戏:struct模块

struct模块里有两个方法:

pack :把任意长度数字转化成具有固定4个字节长度的字节流

unpack :把4个字节值恢复成原来的数字,返回最终的是元组

 struct

 pack i => int 要转化的当前数据是整型
res1 = struct.pack(iprint(res1,len(res1))  b'xffxc9x9a;' 4
res2 = struct.pack(print(res2,len(res2))  b'x01x00x00x00' 4
res3 = struct.pack(print(res3,len(res3))  b'x7f#Cx00' 4 pack 的范围 -2147483648 ~ 2147483647 21个亿左右
res4 = struct.pack() 
print(res4,len(res4))  b'x00u+}' 4


 unpack i => 把对应的数据转换成int整型
tup = struct.unpack(print(tup)  (2100000000,)
print(tup[0])  2100000000

4.解决黏包方式三:使用struct模块

服务端

 struct
sk = 处理收发数据的逻辑
strvar = input(请输入你要发送的数据)
msg = strvar.encode()
length = len(msg)  你输入字符串的长度
res = struct.pack( 无论长度是多少,res都是固定4个字节长度的字节流
print(--- 第一次发送的是字节长度
conn.send(res)

 第二次发送真实的数据
conn.send(msg)

 第三次发送真实的数据
conn.send(世界真美好123.encode())



conn.close()
sk.close()

客户端

 第一次接受的是字节长度
n = sk.recv(4)  接收到4个字节长度的字节流
tup = struct.unpack( 将4个字节长度的字节流转化成数字
n = tup[0]  n就是长度


 第二次接受真实的数据
res = sk.recv(n)
 第三次接受真实的数据
res = sk.recv(1024(res.decode())
sk.close()

struct如何做到控制接受字节数的呢?

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读