目录

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浏览器,电子邮件,文件传输,大量数据传输的场景

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

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

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

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

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()

客户端

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

2.TCP循环发消息

服务端

# ### 服务端
import socket
# 1.创建socket对象
sk = socket.socket()
# # 在bind方法之前加上这句话,可以让一个端口重复使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 2.绑定ip和端口号(在网络中注册该主机)
sk.bind( ("127.0.0.1" , 9000) )
# 3.开启监听
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()
while True:
res = conn.recv(1024)
print(res.decode())
strvar = input("请输入服务端要给客户端发送的内容")
conn.send(strvar.encode())
if strvar.upper() == "Q":
break # 6.四次挥手
conn.close() # 7.退还端口
sk.close()

客户端

# ### 客户端
import socket
# 1.创建socket对象
sk = socket.socket()
# 2.连接服务端
sk.connect( ("127.0.0.1" , 9000) )
# 3.收发数据
"""
res = sk.recv(1024) # 一次最多接受1024个字节
print(res.decode())
""" while True:
strvar = input("请输入您要发送的内容:")
sk.send(strvar.encode())
res = sk.recv(1024)
if res == b"q" or res == b"Q":
break
print(res.decode()) # 4.关闭连接
sk.close()

3.UDP基本语法

服务端

# ### 服务端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
# 2.绑定地址端口号
sk.bind( ("127.0.0.1",9000) )
# 3.udp服务器,在一开始只能够接受数据
msg,cli_addr = sk.recvfrom(1024) print(msg.decode())
print(cli_addr) # 服务端给客户端发送数据
msg = "我是你老娘,赶紧给我回家吃饭"
sk.sendto(msg.encode(),cli_addr) # 4.关闭连接
sk.close()

客户端

# ### 客户端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM) # 2.收发数据的逻辑 # 发送数据
msg = "你好,你是mm还是gg"
# sendto( 消息,(ip,端口号) )
sk.sendto( msg.encode() , ("127.0.0.1",9000) ) # 接受数据
msg,server_addr = sk.recvfrom(1024)
print(msg.decode())
print(server_addr) # 3.关闭连接
sk.close()

4.UDP循环发消息

服务端

# ### 服务端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
# 2.绑定地址端口号
sk.bind( ("127.0.0.1",9000) )
# 3.udp服务器,在一开始只能够接受数据
while True:
# 接受消息
msg,cli_addr = sk.recvfrom(1024)
print(msg.decode())
message = input("服务端给客户端发送的消息是?:")
# 发送数据
sk.sendto(message.encode() , cli_addr) # 4.关闭连接
sk.close()

客户端

# ### 客户端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM) # 2.收发数据的逻辑
while True:
# 发送数据
message = input("客户端给服务端发送的消息是?:")
sk.sendto(message.encode(), ("127.0.0.1",9000) ) # 接受数据
msg,addr = sk.recvfrom(1024)
print(msg.decode("utf-8")) # 3.关闭连接
sk.close()

4.黏包

1.出现黏包的原因

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

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

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

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

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

2.黏包出现的两种情况

黏包现象一:

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

黏包现象二:

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

总结:

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

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

3.黏包的应用场景

解决黏包场景:

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

不需要解决黏包场景:

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

5.解决黏包问题

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

服务端

# ### 服务端
import time
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑 # 先发送接下来要发送数据的大小
conn.send("5".encode()) # 发送5个字节
# 发完长度之后,再发数据
conn.send("hello".encode())
conn.send(",world".encode()) conn.close()
sk.close()

客户端

# ### 客户端
"""
黏包出现的两种情况:
(1) 发送端发送数据太快
(2) 接收端接收数据太慢
"""
import socket
import time
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) 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)
print(res1)
print(res2) sk.close()

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

服务端:

# ### 服务端
import time
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑
# 先发送接下来要发送数据的大小
conn.send("00000100".encode())
# 发完长度之后,再发数据
msg = "hello" * 20
conn.send(msg.encode())
conn.send(",world".encode()) conn.close()
sk.close()

客户端:

# ### 客户端
"""
黏包出现的两种情况:
(1) 发送端发送数据太快
(2) 接收端接收数据太慢
"""
import socket
import time
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) time.sleep(2)
# 处理收发数据的逻辑
# 先接受接下来要发送数据的大小
res = sk.recv(8)
num = int(res.decode())
# 接受num这么多个字节数
res1 = sk.recv(num)
res2 = sk.recv(1024)
print(res1)
print(res2) sk.close()

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

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

3.前戏:struct模块

struct模块里有两个方法:

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

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

import struct

# pack
# i => int 要转化的当前数据是整型
res1 = struct.pack("i",999999999)
print(res1,len(res1)) # b'\xff\xc9\x9a;' 4
res2 = struct.pack("i",1)
print(res2,len(res2)) # b'\x01\x00\x00\x00' 4
res3 = struct.pack("i",4399999)
print(res3,len(res3)) # b'\x7f#C\x00' 4
# pack 的范围 -2147483648 ~ 2147483647 21个亿左右
res4 = struct.pack("i",2100000000)
print(res4,len(res4)) # b'\x00u+}' 4 # unpack
# i => 把对应的数据转换成int整型
tup = struct.unpack("i",res)
print(tup) # (2100000000,)
print(tup[0]) # 2100000000

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

服务端

# ### 服务端
import time
import socket
import struct
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑
strvar = input("请输入你要发送的数据")
msg = strvar.encode()
length = len(msg) # 你输入字符串的长度
res = struct.pack("i",length) # 无论长度是多少,res都是固定4个字节长度的字节流
print("---",res) # 第一次发送的是字节长度
conn.send(res) # 第二次发送真实的数据
conn.send(msg) # 第三次发送真实的数据
conn.send("世界真美好123".encode()) conn.close()
sk.close()

客户端

# ### 客户端
"""
黏包出现的两种情况:
(1) 发送端发送数据太快
(2) 接收端接收数据太慢
"""
import socket
import time
import struct
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) time.sleep(2)
# 处理收发数据的逻辑 # 第一次接受的是字节长度
n = sk.recv(4) # 接收到4个字节长度的字节流
tup = struct.unpack("i",n) # 将4个字节长度的字节流转化成数字
n = tup[0] # n就是长度 # 第二次接受真实的数据
res = sk.recv(n)
print(res.decode()) # 第三次接受真实的数据
res = sk.recv(1024)
print(res.decode())
sk.close()

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

day30:TCP&UDP:socket的更多相关文章

  1. TCP&UDP&Socket讲解(上)

    这两天我将整理TCP&UDP&Socket,大约花大家10-15分钟之间,希望本篇文章让大家对TCP使用的理解提高一个层次. 建议大家拿出纸和笔,画一下!!! 一.TCP 1. TCP ...

  2. TCP/UDP,SOCKET,HTTP,FTP 简析

    (一)TCP/UDP,SOCKET,HTTP,FTP简析 TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层: 网络层:IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议 传 ...

  3. Bash Shell 下打开一个TCP / UDP SOCKET

    Bash Shell 下打开一个TCP / UDP SOCKET http://jingyan.baidu.com/article/636f38bb6166c3d6b84610d1.html

  4. TCP UDP socket http webSocket 之间的关系

    ---恢复内容开始--- OSI&TCP/IP模型 要弄清tcp udp socket http websocket之间的关系,首先要知道经典的OSI七层模型,与之对应的是TCP/IP的四层模 ...

  5. HTTP TCP UDP Socket 关系的几个经典图

      从上图可以看到,TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层. 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 在传输层中有TCP协议与UDP协议. ...

  6. TCP/UDP socket

    TCP socket:有链接,绑定端口,接着去侦听,若有请求,那么accept(),获得新的socket,并且去接收/发送数据报. UDP socket:无连接,不需要侦听,也不用一个新的socket ...

  7. How To: Perl TCP / UDP Socket Programming using IO::Socket::INET

    http://www.thegeekstuff.com/2010/07/perl-tcp-udp-socket-programming/ In this article, let us discuss ...

  8. TCP/UDP Socket调试工具提供了TCP Server,TCP Client,UDP Server,UDP Client,UDP Group 五种Socket调试方案。

    一.TCP通信测试: 1)   创建TCP Server: 选中左方的TCP Server, 然后点击”创建”按钮,软件弹出监听端口输入框 输入监听端口后,即创建了一个在指定端口上进行监听的TCP S ...

  9. tcp/udp socket编程异同

    一.TCP与UDP的区别 基于连接与无连接 对系统资源的要求(TCP较多,UDP少) UDP程序结构较简单 流模式与数据报模式 TCP保证数据正确性,UDP可能丢包 TCP保证数据顺序,UDP不保证 ...

  10. 网络协议中HTTP,TCP,UDP,Socket,WebSocket的优缺点/区别

    先说一下网络的层级:由下往上分为 物理层.数据链路层.网络层.传输层.会话层.表示层和应用层 1.TCP和UDP TCP:是面向连接的一种传输控制协议.属于传输层协议.TCP连接之后,客户端和服务器可 ...

随机推荐

  1. 更多Linux实用命令

    更多实用命令 进程相关 当程序运行在系统上时,我们称之为进程(process).想监测这些进程,需要熟悉 ps/top 等命令的用法.ps 命令好比工具中的瑞士军刀,它能输出运行在系统上的所有程序的许 ...

  2. 解决office提示您的许可证不是正版的问题

    https://blog.csdn.net/d_pcb66/article/details/125339872?spm=1001.2101.3001.6650.4&utm_medium=dis ...

  3. Arduino教程目录

    目录 第一节.安装Arduino开发环境 第二节.第一个HelloWorld 第二节续.LED操作 呼吸灯 流水灯 正在加快制作,大家可以先看下面的视频了解基本语法,我准备基础课程和实际项目结合讲解. ...

  4. STM32任意引脚模拟IIC

    关于模拟I2C,任意接口都可模拟(未全部测试,可能存在特殊情况). 关于SDA_IN与SDAOUT:如下定义: 举例:#define MPU_SDA_IN() {GPIOA->CRL&= ...

  5. STL练习-ACboy needs your help again!

      ACboy was kidnapped!! he miss his mother very much and is very scare now.You can't image how dark ...

  6. url not set

    UrI not set 原因与处理方法 今天下午跑代码时发现,上午能跑的代码下午跑不了了.一直报 Url not set错误. 出现这个问题的主要原因,是因为代码中的@ConfigurationPro ...

  7. 转发 关于Windows安装解压版MySQL出现服务正在启动-服务无法启动的问题

    部分转自 :https://blog.csdn.net/u013901768/article/details/80707307 我是从服务器上复制了mysql的整个目录,到本地,然后怎么也不好用,看了 ...

  8. python学习记录(五)-文件操作

    open()参数说明 ''' 参数1:路径 ./当前目录 ../上一级目录 参数2: 基础模式:w r x a w:写入,不存在则创建,存在则打开,清空文件内容,光标指向最前面 r:只读,不存在则报错 ...

  9. The difference of src and href

    href是Hypertext Reference的缩写,表示超文本引用.用来建立当前元素和文档之间的链接.常用的有:link.a.例如: <link href="reset.css&q ...

  10. c#和JS数据加密(转)

    前台提交按纽 后以赋值后台取值    Base64编解码   C# /* 编码规则 Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编码. 它将需要编码的数据拆分成字节数组. ...