200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > TCP与UDP协议 socket套接字编程 通信相关操作

TCP与UDP协议 socket套接字编程 通信相关操作

时间:2021-12-31 08:43:36

相关推荐

TCP与UDP协议 socket套接字编程 通信相关操作

文章目录

TCP与UDP协议TCP协议==三次握手====四次挥手==UDP协议TCP与UDP的区别应用层socket套接字代码优化循环通信半连接池粘包问题

TCP与UDP协议

本质

规定了数据传输所遵循的规则ps:数据传输能够遵循的协议有很多,TCP和UDP是较为常见的两个

TCP协议

TCP是一种面向广域网的通信协议,目的是在跨越多个网络通信时,为两个通信端点之间提供一条通信方式

三次握手

建立双向通道,中间的 FIN 和 ACK 能合并在一起

ps:洪水攻击(同时让大量的客户端朝服务端建立TCP连接的请求,容易出现状况)

第一次握手:客户端将标志位SYN设置为1,并且随机产生一个值seq=x,并且将该数据包发送给服务器,此刻客户端进入等待状态,等待服务器的确认

第二次握手:服务器接收到数据包之后,标志位是SYN=1,可以知道客户端请求连接,服务端如果同意创建连接,此刻SYN ACK都设置为1,并且ack=x+1,随机产生一个值seq=y,传递给客户端

第三次握手:客户端接收到确认之后,检查ack是否是x+1,ACK是否是1,如果都是正确的,会将标志位ACK设置为1,确认号ack设置为y+1,发送给服务端

四次挥手

断开双向通道

双方各自向对方发起建立连接的请求,再各自给对方回应,只不过,中间的 FIN 和 ACK 不一定能合并在一起

第一次挥手:客户端发送标识位FIN,用来关闭客户端到服务端的连接

第二次挥手:服务器接收到客户端发送的FIN之后,先发送ACK,ack,seq给客户端

第三次挥手:服务器发送FIN给客户端,用来关闭服务器和客户端的数据传送

第四次挥手:客户端接收到FIN之后,发送ACK,ack给服务端

注意:TCP链接是全双工的 ,全双工同时可以在2个方向上传输数据

类似于打电话的一个过程:

UDP协议

本质

基于UDP协议发送数据,没有任何的通道也没有任何的限制,提供面向交易的简单和不可靠的信息传输服务

作用

主要用于支持需要在计算机之间传输数据的网络应用

TCP与UDP的区别

1.UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同

2.TCP 是面向连接的传输控制协议,而UDP 提供了无连接的数据报服务;

3.TCP 具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;UDP 在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,所以会出现分组丢失、重复、乱序;

4.UDP 具有较好的实时性,工作效率较 TCP 协议高;UDP 段结构比 TCP 的段结构简单

应用层

1.应用层的目的:

应用层对应用程序的通信提供服务。

2.应用层重要协议:

FTP、SMTP和POP3、HTTP、DNS。

3.应用层的功能:

① 文件传输。访问和管理。

② 电子邮件。

③ 虚拟终端。

④ 查询服务和远程作业登录。

socket套接字

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

基于文件类型的套接字家族套接字家族的名字:AF_UNIX基于网络类型的套接字家族套接字家族的名字:AF_INET

理解图:

运行程序的时候 肯定是先确保服务端运行 之后才是客户端

服务端:

import socket# 1.创建一个socket对象server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字# 2.绑定一个固定的地址(ip\port)server.bind(('127.0.0.1', 8080)) # 127.0.0.1本地回环地址(只允许自己的机器访问)# 3.半连接池(暂且忽略)server.listen(5)# 4.开业 等待接客sock, address = server.accept()print(sock, address) # sock是双向通道 address是客户端地址# 5.数据交互sock.send(b'hello big baby~') # 朝客户端发送数据data = sock.recv(1024) # 接收客户端发送的数据 1024bytesprint(data)# 6.断开连接sock.close() # 断链接server.close() # 关机

客户端:

import socket# 1.产生一个socket对象client = socket.socket()# 2.连接服务端(拼接服务端的ip和port)client.connect(('127.0.0.1', 8080))# 3.数据交互data = client.recv(1024) # 接收服务端发送的数据print(data)client.send(b'hello sweet server') # 朝服务端发送数据# 4.关闭client.close()

服务端套接字函数

s.bind() 绑定(主机,端口号)到套接字

s.listen() 开始TCP监听

s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数

s.connect() 主动初始化TCP服务器连接

s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

s.recv() 接收TCP数据

s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)

s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余

空间时,数据不丢失,循环调用send直到发完)

s.recvfrom() 接收UDP数据

s.sendto() 发送UDP数据

s.getpeername() 连接到当前套接字的远端的地址

s.getsockname() 当前套接字的地址

s.getsockopt() 返回指定套接字的参数

s.setsockopt() 设置指定套接字的参数

s.close() 关闭套接字

面向锁的套接字方法

s.setblocking() 设置套接字的阻塞与非阻塞模式

s.settimeout() 设置阻塞套接字操作的超时时间

s.gettimeout() 得到阻塞套接字操作的超时时间

面向文件的套接字的函数

s.fileno() 套接字的文件描述符

s.makefile() 创建一个与该套接字相关的文件

代码优化

循环通信

引言

主要用来打破信息只能传输一次的问题

方法

1.先解决消息固定的问题

解决办法:利用input获取用户输入

2.再解决通信循环的问题

解决方法:将双方用于数据交互的代码循环起来

代码实现

while True:data = sock.recv(1024) # 听别人说话print(data.decode('utf8'))msg = input('请回复消息>>>:').strip()sock.send(msg.encode('utf8')) # 回复别人说的话while True:msg = input('请输入你需要发送的消息>>>:').strip()client.send(msg.encode('utf8')) # 给服务端发送消息data = client.recv(1024) # 接收服务端回复的消息print(data.decode('utf8'))

输入的消息不能为空(主要是针对客户端)

解决方法:判断是否为空,为空则重新输入

服务端能够持续提供服务

需求:不会因为客户端断开连接而报错解决方法:异常捕获 一旦客户端断开连接 服务端结束通信循环 调到连接处等待

半连接池

1.什么是半连接池:

当服务器在响应了客户端的第一次请求后会进入等待状态,会等客户端发送的ack信息,这时候这个连接就称之为半连接

2.本质

半连接池其实就是一个容器,系统会自动将半连接放入这个容器中,可以避免半连接过多而保证资源耗光

3.产生半连接的两种情况:

客户端无法返回ACK信息

服务器来不及处理客户端的连接请求

粘包问题

问题:

我们知道tcp容易产生黏包的问题,而udp不会产生黏包的问题,但是会产生丢包的问题,tcp应用的场景很多所以黏包问题必须要解决。(传输信息字节大小和允许接收信息字节大小的问题)

1.TCP特性

流式协议:所有的数据类似于水流 连接在一起的ps:数据量很小 并且时间间隔很多 那么就会自动组织到一起

2.recv

我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包

解决方法一

1.解决黏包问题第一种方法,我们知道黏包问题是由于tcp的优化算法将两个不太大的数据包合并了一起发送的,这种情况一般出现在连续使用几个send()出现的,所以我们如果知道要发送的数据有多大我们就可以设置接收的大小,这样就可以刚好能把所有的数据接收完。下面是具体的步骤细节见代码

这是远程执行cmd命令并返回结果的程序server端代码import structimport socketsk = socket.socket()sk.bind(('127.0.0.1',8080))sk.listen()conn,addr = sk.accept()while True:cmd = input('>>>')conn.send(bytes(cmd,encoding='utf-8'))num = conn.recv(1024).decode('utf-8') #接收client端计算好的数据长度conn.send(bytes('ok',encoding='utf-8'))#发送一个确认防止发送num的时候跟后面的send内容合并了ret = conn.recv(num)print(ret.decode('gbk'))conn.close()sk.close()

client端代码import socketimport structimport subprocesssk = socket.socket()sk.connect(('127.0.0.1',8080))while True:cmd = sk.recv(1024).decode('utf-8')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = ret.stdout.read()std_err = ret.stderr.read()sk.send(bytes(str(len(std_err)+len(std_out)),encoding='utf-8'))#上面计算字符串的长度发送给server端在接收的时候刚好接收那么长的数据sk.recv(1024) #ok 这一步主要的目的是为了将num的发送跟后面的send分割开防止黏包现象sk.send(std_out)sk.send(std_err)sk.close()

总结:但是有一个问题就是多了一次连接延时要接收一个没有用的数据 ok,如何不需要接收ok就能解决黏包现象呢?这就需要下面这种解决方案.

解决方法二

2.用struct模块解决黏包现象

server端代码#tcp黏包现象的解决 structimport structimport socketsk = socket.socket()sk.bind(('127.0.0.1',8080))sk.listen()conn,addr = sk.accept()while True:cmd = input('>>>')conn.send(bytes(cmd,encoding='utf-8'))num = conn.recv(1024) #接收数据num = struct.unpack('i',num)[0]#进行解包,解包的结果是一个元组类型取第一个数据ret = conn.recv(num)print(ret.decode('gbk'))conn.close()sk.close()

client端代码import socketimport structimport subprocesssk = socket.socket()sk.connect(('127.0.0.1',8080))while True:cmd = sk.recv(1024).decode('utf-8')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = ret.stdout.read()std_err = ret.stderr.read()num = len(std_err) + len(std_out)num = struct.pack('i',num) #利用struct模块将一个数据转换成bytes类型 i代表int型sk.send(num)sk.send(std_out)sk.send(std_err)sk.close()

总结:

struct模块无论数据长度是多少 都可以帮你打包成固定长度

然后基于该固定长度 还可以反向解析出真实长度

struct模块针对数据量特别大的数字没有办法打包!!!

思路:

服务端

1.先将真实数据的长度制作成固定长度 4

2.先发送固定长度的报头

3.再发送真实数据

客户端

1.先接收固定长度的报头 4

2.再根据报头解压出真实长度

3.根据真实长度接收即可

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。