TCP\UDP协议 socket模块
传输层主要协议
传输层有很多协议,比如TCP、UDP协议。
TCP与UDP都是用来规定通信方式的
通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊
随性所欲的聊:文字 图片 视频 小油腻话 你侬我侬
遵循一些协议:开头带尊称 首行空两格 只准用官话 不能打情骂俏
ps:不遵循上述协议也可以通信 只不过遵循了更合规合法合理!!!
TCP协议
TCP协议也称为流式协议、可靠协议(数据不容易丢失)
三次握手
- 经过这两步:C朝S建立了通道,这个通道是单向的,C可以给S发数据!
- 如果S想给C发数据怎么办?又要走两步!才能建立S到C的通道。
注意:C给S发信息就走通道1,S给C发信息走通道2,这两个通道互不干扰!两个通道都是单向的! - 这不是四次吗?为什么说是三次握手?
因为中间两次可以整合为一次,也就是两条信息一起发了!总得来说只需要通信三次!这就是三次握手 建链接!
附上图片:
TCP协议反馈机制
TCP协议之所以比UDP协议可靠 是建立了双向通道 对吗?
TCP协议之所以比UDP协议可靠 原因在与TCP协议发送消息有反馈机制!
(你必须要回复说,你收到了。如果你不回复,我就再次给你发同一份数据!)
基于TCP发送的消息会在本地先保存该消息 如果地方确认收到才会删除 否则在一定的时间内会频繁的多次发送直到确认或者超时为止。
我发给你一份 内存里也临时存一份 当你不回复,我隔个5秒10秒再给你发,如果我坚持给你发了30分钟,你都不回复,我就默认你嗝屁了, 就把临时保存的数据删掉。
另外当你回复:收到了! 我也会把内存里临时存的数据删掉。
# 也就是需要一个确认 确认通过了才会继续发 这就是反馈机制。
四次挥手
由任意一方发起断开的请求:如图C发送断开的请求,C同意。所以将会断开C到S的通道。
再经历两步,断开S到C的通道:
中间的两步为什么不能合并?
因为C已经没有数据给S了 ,但是S可能还有一些数据需要给C,服务器要确认自己的信息是否发完(TIME_WAIT)。所以S需要一个缓冲时间,无法立刻做出断开通道的决定,这中间是有一个检查时间(缓冲时间)的!
隐喻:
# 三次握手
男:做我女朋友!(建立链接)
女:同意!你也做我男朋友!(同意建立,并发起请求)
男:同意!(同意建立)
# 四次挥手
女:分手吧!(不跟你发消息)
男:同意,分手之后做朋友(还跟你发消息)
男:算了吧,还是别联系(断开请求)
女:6 (同意断开)
洪水攻击
洪水攻击:同一时间有大量的客户端请求建立链接 会导致服务端一直处于SYN_RCVD状态
如何抵御洪水攻击?建立半连接池
服务端如何区分客户端建立链接的请求:
1.通过ip
2.通过SYN 如SYN seq=x
x y表示不同客户端、服务端的唯一标识。
UDP协议
# UDP协议
丢包协议、不可靠协议
彼此之间不做任何的链接 不做通道 不二次做确认操作
特点:不需要建立双向通道 数据的传输速度快 但是可能会丢失
qq使用的就是UDP协议 所有可能会丢失
但是我们可以在UDP协议的基础之上做很多额外的扩展来保证数据的安全
'''
TCP协议类似于打电话:你一句我一句 你侬我侬
UDP协议类似于发短信:发了之后不管你看不看 只要发了就行
'''
socket模块
# 编写一个CS架构的程序 实现数据交互
思考:
需要编写代码 操作OSI七层 相当复杂!
就跟操作系统 编写操作硬件的代码一样
由于操作OSI七层 是所有CS架构的程序都需要经历的过程 所以为了方便 产生了socket技术。
在OSI七层中增加了socket抽象层,用socket提供的快捷方式操作其他层
python中socket模块>>>为操作OSI七层协议提供了快捷方式 不需要自己处理一遍
socket也叫套接字,有以下两种分类:
基于文件类型的套接字家族(单机)
AF_UNIX
基于网络类型的套接字家族(联网)
AF_INET
socket代码简介
'''服务端'''
import socket
"""
以后要养成查看源码编写代码的思路
"""
# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket() # 括号内不写参数 默认就是TCP协议 family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条件)
server.bind(('127.0.0.1', 8080)) # 127.0.0.1为本地回环地址 只有自己的电脑可以访问
# 3.设立半连接池(暂且忽略)
server.listen(5)
# 4.等待接客
sock, addr = server.accept() # return sock, addr 三次握手
print(sock, addr) # sock就是双向通道 addr就是客户端地址
# 5.服务客人
data = sock.recv(1024) # 接收客户端发送过来的消息 1024字节
print(data.decode('utf8'))
sock.send('尊敬的客人 您说什么就是什么 一切按照您的要求来'.encode('utf8')) # 给客户端发送消息 注意消息必须是bytes类型
# 6.关闭双向通道
sock.close() # 四次挥手
# 7.关闭服务端
server.close() # 店倒闭了
'''客户端'''
import socket
# 1.生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务端的地址链接服务端
client.connect(('127.0.0.1', 8080))
# 3.直接给服务端发送消息
client.send('大爷有钱 把你们店最好的给我叫出来'.encode('utf8'))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
client.close()
socket.socket()
查看源码:
socket.socket() # 会产生一个sockct对象
# 默认情况下socket对象 ---> TCP + 网络型套接字
family = AF_INET # AF_INET的意思就是 基于网络的socket
type = SOCK_STREAM # SOCK_STREAM 流式协议 也就是TCP协议 # SOCK_DGRAM UDP协议
server = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM) # 实现UDP协议
server.bind()
查看源码:
server.bind(('127.0.0.1', 8080))
'''
bind方法用于给服务端绑定一个固定的地址
接受一个元祖 (IP地址,端口号)
'''
server.accept()
sock, addr = server.accept() # return sock, addr
# 示例 <socket.socket fd=600, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.81', 8080)
'''
accept有两个返回值:
sock表示客户端和服务端之间的管道 可以使用sock对象发送信息等
addr是访问服务端的客户端IP地址
addr是给你打电话的人的地址
sock是电话的听筒 你可以基于听筒跟别人交流!
如果没有客人上门 accept这行代码会一直等待 程序会卡在这里(listen)。
'''
sock.recv() sock.send()
sock.recv(1024) # 接受
sock.send() # 发送
'''
recv方法用于接收客户端发送过来的消息 如果 没有消息 或者 消息为空 程序都会卡这行代码
这里的数字1024 表示接受1024个字节 多了就抛弃
注意发送和接受的都是bytes类型 要对输入进行转码
'''
sock.close() server.close()
sock.close() # 关闭通道
server.close() # 关闭服务端
这两行代码也可以不写 因为socket模块有自动关闭的操作。
client.connect()
client.connect(('127.0.0.1',8080))
'''
connect方法用于服务端链接客户端
传入一个元祖:(要访问的服务端IP,服务端端口号)'''
问题和优化
1.聊天内容自定义
针对消息采用input获取
2.让聊天循环起来
将聊天的部分用循环包起来
用户输入的消息不能为空
本质其实是两边不能都是recv或者send 一定是一方收一方发
相当于两边电话通了,但是都不说话,都在等着别人说话= =
可以在服务端加限制,禁止传入空字符串。
服务端多次重启报错
Address already in use 主要是mac电脑会报
解决1:改C/S双方使用的端口号
解决2:加代码
添加如下红框代码:
#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)
客户端异常断开
当客户端异常断开的情况下 如何让服务端继续服务其他客人?
客户端如果异常断开 服务端代码应该重新回到accept等待新的客人!
如果是windows电脑 停掉客户端 服务端会直接报错
如果是mac、linux则不会报错 服务端会接受到一个空消息。
所以对mac、linux:
如果是window,则需要异常处理,捕获到因为服务端断掉而产生的报错:
但是服务端也无法继续服务客户了,还是没回到accept,所有只能再套一个循环:
半连接池
当有多个客户端来链接的情况下 我们可以设置等待数量(不考虑并发问题)
server.listen
server.listen(5)
'''
用于构造半连接池
比如海底捞排队 给门外摆了5把椅子
'''
示例
使得一个py文件可以运行多次:
让多个服务端和客户端产生链接:
绿色的框内有:1个正在被服务的客人 5个在椅子上等待的客人
红色框:2个被拒之门外的客人
如果在报错之前 有客人退出 则会有椅子空出 也就能再坐一个人。
练习
UDP单向通信
# 发送方
import socket
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
client.bind(('192.168.1.81', 8084)) # 发送方绑定端口 避免随机分配
while True: # 192.168.1.81是本机地址
user_input = input('请输入>>>:')
client.sendto(user_input.encode('utf8'), ('192.168.1.81', 8080)) # 循环给8080端口发送信息 # sendto(bytes类型数据,目标地址元祖)
if user_input == 'q':
break
# 接收方
import socket
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
server.bind(('192.168.1.81', 8080)) # 不能重复绑定
while True:
date, addr = server.recvfrom(1024) # recvform返回(byte类型数据,发送方ip地址的元祖)
print(f'来自:{addr}的信息>>>:{date.decode("utf8")}')
模拟QQ聊天
首先对于客户端,在不使用多进程的情况下,无法做到一个py文件既发送信息,又接受信息。因为程序的循环会阻塞在input处等待输入,实现不了及时聊天的效果。
推导1、那么可以使用两个py文件,发送.py都使用UDP协议:
这样无论是客户1\客户2的接受.py文件都可以有及时聊天的效果。
推导2、中转
在服务端可以产生及时聊天的效果。
ps:主要还是单进程实现不了又输入又接受的操作,因为不论是recv还是input都是阻塞的,所以想实现QQ聊天只能使用多进程。
总结
window下:
对于python的socket模块:
如果是建立TCP链接,没有server.listen会报错:
OSError: [WinError 10022] 提供了一个无效的参数。
没有server.accept就产生不了管道,也会报错。所以使用socket模块创建TCP链接listen和accept缺一不可。
如果是建立UDP通信则只需要指定你要发送的地址即可。
建立tcp、udp链接时,可以不进行bind绑定,如果这样系统就会给你当前进程,随机分配一个端口号。
又回来总结
注意:
accept 等待的是一个新的进程
sock 是跟某个进程建立的管道
对于这行代码:
with open('inf.txt', 'rb') as f:
for line in f:
client.send(line)
将txt文件用rb二进制模式打开,直接用send方法发送时,此时发送的bytes是16进制的,在接收时无法直接decode('utf8')
需要使用hex()方法:
with open('info2.txt', 'wb') as f:
data = sock.recv(1024).hex() # 将16进制bytes b'\xaa'转换成utf-8 str 'aa'
f.write(data.encode('utf8'))
# 用rb模式打开文件 得到的是16进制的bytes类型 这种类型是不能用decode('utf8')解码的
# 可以用hex方法,将16进制bytes b'\xaa'转换成utf-8 str 'aa'
# bytes类型内部有哪些分类?
参考:
https://blog.csdn.net/zbb19/article/details/123991426
https://www.cnblogs.com/oreoz/p/16892930.html
https://www.cnblogs.com/zkz0206/p/16897399.html
TCP\UDP协议 socket模块的更多相关文章
- 网络编程—网络基础概览、socket,TCP/UDP协议
网络基础概览 socket概览 socket模块—TCP/UDP的实现 TCP/UDP总结 网络基础概览 osi七层协议各层主要的协议 # 物理层传输电信号1010101010 # 数据链路层,以太网 ...
- QQ--基于TCP/UDP协议的通讯原理
QQ是一个基于TCP/UDP协议的通讯软件 发送消息的时候是UDP打洞,登陆的时候使用HTTP~因为登陆服务器其实就是一个HTTP服 务器,只不过不是常用的那些,那个服务器是腾讯自行开发的! 一 ...
- java 通过TCP\UDP 协议实现多人聊天,点对点,文件传送-----分服务器端和客户端
java 通过TCP\UDP 协议实现多人聊天,点对点,文件传送-----分服务器端和客户端 启动界面如下图: 首先启动服务器: 客户端登陆,登陆成功后为: 默认发送是全部用户,是多人发送. 当在边列 ...
- UDP协议&socketserver模块
UDP协议&socketserver模块 一.UDP协议 1.1 UDP实现简单通信 服务器 ------------------------------------------------- ...
- TCP/UDP协议(二)
面试问题:Tcp/Udp协议是什么,各有什么异同点,各自的使用场景? Tcp协议(传输控制协议) tcp是面向连接的协议,在收发数据之前,必须与对方建立可靠的连接: 三次握手:简单形象通俗描述: 主机 ...
- TODO:Golang语言TCP/UDP协议重用地址端口
TODO:Golang语言TCP/UDP协议重用地址端口 这是一个简单的包来解决重用地址的问题. go net包(据我所知)不允许设置套接字选项. 这在尝试进行TCP NAT时尤其成问题,其需要在同一 ...
- TCP/UDP协议简要梳理
TCP/UDP协议简要梳理 TCP TCP,Transmission Control Protocol,传输控制协议是一种面向连接的.可靠的.基于字节流的传输层通信协议.在因特网协议族中,TCP所在的 ...
- Shell 脚本实现TCP/UDP协议通讯
Shell 脚本实现TCP/UDP协议通讯 http://www.cnblogs.com/occult/archive/2012/12/25/2832183.html
- python socket原理 及socket如何使(tcp udp协议)
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API) 主要内容: 1.基于 ...
- TCP/UDP协议、理解三次握手四次挥手、Socket
一.什么是socket? 中文名叫套接字,是对底层的 TCP IP UDP 等网络协议进行封装,使得上层的应用程序开发者,不用直接接触这对复杂,丑陋的协议. 在程序员的言论,他就是一个封装好的模块,要 ...
随机推荐
- 01_Typora学习
Typora学习 使用Typora 编辑器 一. 标题 一个#后加空格表示一级标题(快捷键Ctrl+1) 两个#后加空格表示二级标题(快捷键Ctrl+2) 以此类推,目前最多到六级标题(快捷键Ctrl ...
- [CG从零开始] 5. 搞清 MVP 矩阵理论 + 实践
在 4 中成功绘制了三角形以后,下面我们来加载一个 fbx 文件,然后构建 MVP 变换(model-view-projection).简单介绍一下: 从我们拿到模型(主要是网格信息)文件开始,模型网 ...
- POJ3662 [USACO08JAN]Telephone Lines (二分答案/分层图求最短路)
这道题目有两种解法: 1.将每个点视为一个二元组(x,p),表示从起点到x有p条路径免费,相当于构建了一张分层图,N*k个节点,P*k条边.在这张图上用优先队列优化的SPFA算法求解,注意这里的d数组 ...
- 驱动开发:内核通过PEB得到进程参数
PEB结构(Process Envirorment Block Structure)其中文名是进程环境块信息,进程环境块内部包含了进程运行的详细参数信息,每一个进程在运行后都会存在一个特有的PEB结构 ...
- 结构体struct知识
2022-10-12 08:52:03 // 结构体知识#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>#include<m ...
- resultMap处理字段和属性的映射关系
1.resultMap处理字段和属性的映射关系 若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射 <!-- resultMap:设置自定义映射 属性: id:表示自定 ...
- Vue学习之--------插槽【默认插槽、具名插槽、作用域插槽】(2022/8/30)
插槽Vue.js官网介绍:https://vuejs.org/guide/components/slots.html 会牵涉到template的用法.占位.实际不渲染到页面中 1.默认插槽: 1.1 ...
- 15行python代码实现人脸识别
方法一:face_recognition import cv2 import face_recognition img_path = "C:/Users/CJK/Desktop/1.jpg& ...
- 题解UVA10948 The primary problem
前言 前置 \(\sf{Solution}\) 既然有了 \(n\) ,那找出 \(a\) 和 \(b\) 就只要枚举 \(a\) 的范围 \(1\sim n\),判断 \(a\) 和 \(n-a\) ...
- 题解 P2080 增进感情
\(\sf Link\) 爆搜最香了. 感觉有点像01背包(? 对于每件事,我们可以选择干或者不干,如果干就将好感值处理一下,当所有的事都搜完之后,记录最小值\(minn\) . 最终答案就是\(mi ...