python socket编程
2020-12-13 16:55
标签:七层 简单 close 端口 lis 系统 通过 条件 影响 服务器升级版 客户端升级版 windows解决上面异常的方法: Linux解决方法: 八、基于UDP的套接字 九、基于TCP的套接字,UDP的套接字实现远程执行命令 TCP服务端 TCP客户端 UDP服务端 UDP客户端 十、为什么TCP 会粘包 TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。 UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。 由于TCP无消息保护边界, 需要在消息接收端处理消息边界问题。也就是为什么我们以前使用UDP没有此问题。 反而使用TCP后,出现少包的现象 上面说了原理,但可能有人使用TCP通信会出现多包/少包,而一些人不会。那么我们具体分析一下,少包,多包的情况。 正常情况,发送及时每消息发送,接收也不繁忙,及时处理掉消息。像UDP一样.
发送粘包,多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包. 这种情况和客户端处理繁忙,接收缓存区积压,用户一次从接收缓存区多个数据包的接收端处理一样。 发送粘包或接收缓存区积压,但用户缓冲区大于接收缓存区数据包总大小。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的 发送粘包或接收缓存区积压, 用户缓存区是数据包大小的整数倍。 此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。 发送粘包或接收缓存区积压, 用户缓存区不是数据包大小的整数倍。 此时需要考虑处理一次处理多数据包的情况,同时也需要考虑数据包不完整。 我们的情况就属于最后一种,发生了数据包不完整的情况。 1、发送端需要等缓冲区满才发送出去,造成粘包 2、接收方不及时接收缓冲区的包,造成多个包接收 UDP不需要处理,免的忘记了。 十一、解决TCP粘包 TCP服务端 TCP客户端 python socket编程 标签:七层 简单 close 端口 lis 系统 通过 条件 影响 原文地址:https://www.cnblogs.com/linglinglingling/p/11221050.html一、客户端/服务器架构
即C/S架构,包括:
1、硬件C/S架构(打印机)
2、软件C/S架构(web架构)
互联网中处处是C/S架构(黄色网站是服务端,你的浏览器是客户端;腾讯作为服务端为你提供视频,你得下
腾讯视频客户端才能看视频)
C/S架构与socket的关系;我们学习socket就是为了完成C/S架构的开发。
二、osi七层
引子:
须知一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,
一台计算机系统就可以自己跟自己玩了(打个单机游戏...)
如果你要跟别人一起玩,那你就需要上网了(访问黄色网站,发个朋友圈)
互联网的核心就是有一堆协议组成,协议就是标准,全世界的通讯标准是英语,
如果把计算机比作人,互联网协议就是计算机界的英语。所有的计算机都学会了互联网协议,
那所有的计算机都可以按照统一的标准去发收信息从而完成通讯了,
人们按照分工不同把互联网协议从逻辑上划分了层级。
为何学习socket一定要学习互联网协议:
1、首先:本节课程的目标就是教会你如何基于socket编程,来开发一款自己的C/S架构软件
2、其次:C/S架构1的软件(软件属于应用层)是基于网络进行通讯的。
3、然后:网络的核心即一堆协议,协议即标准,你想要开发一款基于网络通讯的软件,就必须遵守这些标准。
4、最后:就让我们从这些标准开始研究,开启我们的socket编程之旅。
三、socket层
四、socket是什么
socket是应用层与TCP/IP协议通讯的中间软件抽象层,它是一组接口,在设计模块中,socket其实就是一门模式
它把复杂TCP/IP协议隐藏在socket接口后面,对于用户来说,一组简单的接口就是全部,让socket去组织数据,
以符合指定的协议。所以我们无需深入理解TCP/UDP协议,socket已经为我们分装好了,
我们只需要遵循socket的规定去编程,写出的程序自然就是遵循TCP/UDP标准的。
五、套接字发展史及分类
套接字起源于20世纪70年代加利福尼大学伯克利分校版本的Unix,即人们所说的 BSD Unix,因此,
有些人也把套接字称为“伯利克套接字” 或 “BSD套接字”,一开始,套接字设计用在一台主机上多个应用程序
之间的通讯,或TPC,套接字有两种(或者称为有两个种族),分别是基于文件型和基于网络型
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用就是底层文件系统来取数据,两个套接字进程运行在同一机器,可以通过
同一个文件系统间接完成通讯。
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(由于我们只关心网络编程,所以大部分时候我们只使用AF_INET)
六、套接字工作流程
七、基于TCP的套接字
# 创建一个服务器
import socket # 导入 socket 模块
# 创建 socket 对象
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 绑定IP地址和端口
phone.bind(("127.0.0.1",8080))
# 等待客户端连接
phone.listen(5)
# 建立客户端连接
coon,addr = phone.accept()
# 接受数据
coon.recv(1024)
# 发送数据
coon.send("sb".encode("utf-8"))
# 关闭连接
coon.close()
phone.close()
# 创建客户端
import socket # 导入 socket 模块
# 创建 socket 对象
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 链接的IP地址和端口
phone.connect(("127.0.0.1",8080))
# 发送数据
phone.send("sb".encode("utf-8"))
# 接收数据
a = phone.recv(1024)
print(a) # 打印
# 关闭连接
phone.close()
from socket import * # 导入socket模块
ip = ("127.0.0.1",8080) # 要绑定的ip和端口
back_log = 5 # 线程数
buffer_size = 1024 # 字节
a = socket(AF_INET,SOCK_STREAM) # 创建socket对象
a.bind(ip) # 绑定ip和端口
a.listen(back_log) # 线程
while True: # 循环
print("服务端开始运行了") # 打印信息
conn,addr = a.accept() # 建立客户端连接
while True: # 循环
try: # 处理异常
data = conn.recv(buffer_size) # 接收信息
print("客户端发来的信息:",data.decode("utf-8")) # 打印信息
conn.send(data.upper()) # 发送信息
except Exception: # 处理异常
break # 处理异常逻辑,结束循环
conn.close() # 关闭连接
a.close() # 关闭连接
from socket import * # 导入socket模块
ip = ("127.0.0.1",8080) # 要绑定的ip和端口
buff_size = 1024 # 字节
a = socket(AF_INET,SOCK_STREAM) # 创建socket对象
a.connect(ip) # 链接的ip和端口
while True: # 循环
msg = input(">>>") # 等待用户输入
if not msg:continue # 判断 msg ,如果msg为空则跳出本次循环,进入下一次循环
a.send(msg.encode("utf-8")) # 发送信息
print("客户端已经发送信息") # 打印信息
data = a.recv(buff_size) # 接收信息
print("服务端发来的信息:",data.decode("utf-8")) # 打印信息
# 在bind()前面加上
a.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 加入一条socket配置,重用ip和端口
from socket import *
ip = ("127.0.0.1",8080) # 要绑定的ip地址和端口
back_log = 5 # 线程数
buffer_size = 1024 # 节字
a = socket(AF_INET,SOCK_STREAM) # 创建socket对象
a.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 加入一条socket配置,重用ip和端口
a.bind(ip) # 绑定ip和端口
a.listen(back_log) # 线程
while True: # 循环
print(‘服务端开始运行了‘) # 打印信息
conn,addr=a.accept() # 建立客户端连接
while True: # 循环
try: # 处理异常
data = conn.recv(buffer_size) # 接收信息
print("客户端收到的信息:", data.decode("utf-8")) # 打印信息
conn.send(data.upper()) # 发送信息
except Exception: # 处理异常
break # 结束循环
conn.close() # 关闭连接
a.close() # 关闭连接
根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒,TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务
发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决,
vi /etc/sysctl.conf http://www.server-cn.com/
编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
然后执行 /sbin/sysctl -p 让参数生效。 h
p://www.server-cn.com/
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; http://www.server-cn.com/
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
# 服务端
import socket # 导入socket模块
a = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 创建socket对象
ip = ("127.0.0.1",8080) # ip和端口
a.bind(ip) # 绑定ip和端口
while True: # 通讯循环
data,addr = a.recvfrom(1024) # 接受数据 data是数据 addr是客户端ip和端口
print("收到服务端信息:%s %s"%(data,addr)) # 打印信息
a.sendto(data.upper(),addr) # 发送数据
a.close() # 关闭链接
# 客户端
import socket # 导入socket
a = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建socket对象
ip = ("127.0.0.1",8080) # ip和端口
while True: # 通讯循环
msg = input(">>>:") # 等待用户输入
a.sendto(msg.encode("utf-8"),ip) # 发送数据
data,addr = a.recvfrom(1024) # 接受数据 data是数据 addr是服务端ip和端口
print("收到服务端信息:%s %s"%(data,addr)) # 打印信息
a.close() # 关闭链接
import socket # 导入socket模块
import subprocess # 导入subprocess模块
a = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 创建socket对象
ip = ("127.0.0.1",8080) # ip和端口
a.bind(ip) # 绑定ip和端口
a.listen(5) # 线程数
while True: # 链接循环
conn,addr = a.accept() # 等待用户链接
print("接入链接%s"%conn) # 打印信息
while True: # 通讯循环
try: # 处理异常
data = conn.recv(1024) # 接收信息
print("接收到客户端的信息:%s"%data) # 打印信息
if not data:break # 判断 data为空则结束循环
eer = subprocess.Popen(data.decode("utf-8"),shell=True, #创建subprocess对象
stderr = subprocess.PIPE, # 异常
stdout = subprocess.PIPE, # 输出
stdin = subprocess.PIPE) # 输入
res = eer.stderr.read() # 读异常信息并赋值给res
if res: # 判断res为真则执行
res_a = res # res为真则执行
else: # 否则执行else
res_a = eer.stdout.read()
if not res_a: # 判断res_a 为空则执行
res_a = ("执行成功".encode("utf-8")) # 打印信息
conn.send(res_a) # 发送信息
except Exception: # 处理异常
break # 结束循环
conn.close() # 关闭链接
a.close() # 关闭链接
import socket # 导入socket模块
a = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 创建socket对象
ip = ("127.0.0.1",8080) # ip和端口
a.connect(ip) # 链接的ip和端口
while True: # 通讯循环
msg = input(">>>:") # 等待用户输入
if not msg:continue # 判断msg为空则跳出循环,重新循环
if msg == "quit":break # 判断msg == "quit" 则结束循环
a.send(msg.encode("utf-8")) # 发送信息
print("已发送信息") # 打印信息
data = a.recv(1024) # 接受信息
print("服务端发来的信息:%s"%data.decode("utf-8")) # 打印信息 # Linux "utf-8" wid "gbk"
a.close() # 关闭链接
import socket # 导入socket模块
import subprocess # 导入subprocess模块
a = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 创建socket对象
ip = ("127.0.0.1",8083) # ip和端口
a.bind(ip) # 绑定ip和端口
while True: # 通讯循环
data,addr = a.recvfrom(1024) # 接收信息 data是数据 addr是客户端ip和端口
print("收到客户端信息:%s"%data) # 打印信息
res = subprocess.Popen(data.decode("utf-8"),shell=True, # 创建subprocess对象
stderr = subprocess.PIPE, # 异常
stdout = subprocess.PIPE, # 输出
stdin = subprocess.PIPE) #输入
eer = res.stderr.read() # 读异常并赋值给eer
if eer: # eer为真则执行,否则执行else
res_a = eer
else:
res_a = res.stdout.read()
if not res_a: # res_a 为空则执行
res_a = ("执行成功".encode("utf-8"))
a.sendto(res_a, addr) # 发送数据
a.close() # 关闭链接
import socket # 导入socket模块
a = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 创建socket对象
ip = ("127.0.0.1",8083) # ip和端口
while True: # 通讯循环
msg = input(">>>:") # 等待用户输入
if not msg:continue # msg为空则跳出本次循环,重新循环
if msg == "quit":break # msg == "quit" 则结束循环
a.sendto(msg.encode("utf-8"),ip) # 发送信息
data,addr = a.recvfrom(1024) # 接收信息
print("服务端发来的信息:%s"%data.decode("gbk")) # 打印信息 Linux "utf-8" wid "gbk"
a.close() # 关闭链接
粘包的分析
import socket # 导入socket模块
import subprocess # 导入subprocess模块
import struct # 导入struct模块
a = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 创建socket对象
ip = ("127.0.0.1",8080) # ip和端口
a.bind(ip) # 绑定ip和端口
a.listen(5) # 线程数
while True: # 链接循环
conn,addr = a.accept() # 等待用户链接
print("接入链接%s"%conn) # 打印信息
while True: # 通讯循环
try: # 处理异常
data = conn.recv(1024) # 接收信息
print("客户端发来信息:%s"%data) # 打印信息
if not data:break # 判断 data 为空则结束循环
eer = subprocess.Popen(data.decode("utf-8"),shell=True, # 创建subprocess对象
stderr = subprocess.PIPE, # 异常信息
stdout = subprocess.PIPE, # 输出信息
stdin = subprocess.PIPE) # 输入信息
res = eer.stderr.read() # 读异常信息并赋值给res
if res: # 判断 res为真则执行 否则执行else
res_a = res
else:
res_a = eer.stdout.read()
if not res_a: # 判断 res_a 为空则执行
res_a = ("执行成功".encode("utf-8"))
lenth = len(res_a) # 取res_a的长度并赋值给lenth
lenht = struct.pack("i",lenth) # 以int类型打包lenth并赋值给lenht
conn.send(lenht) # 发送信息
conn.send(res_a) # 发送信息
except Exception: # 处理异常
break # 结束循环
conn.close() # 关闭链接
a.close() # 关闭链接
import socket # 导入socket模块
import struct # 导入struct模块
a = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 创建socket对象
a.connect(("127.0.0.1",8080)) # 链接ip和端口
while True: # 通讯循环
msg = input(">>>:") # 等待用户输入
if not msg:continue # 判断 msg 为空则跳出本次循环,重新循环
if msg == "quit":break # 判断 smg == "quit"则结束循环
a.send(msg.encode("utf-8")) # 发送信息
print("客户端已发送信息") # 打印信息
data = a.recv(4) # 接收信息
lenth = struct.unpack("i",data)[0] # 以int类型解包并赋值给lenth
res_size = 0 # 设置变量
res_msg = b"" # 设置变量
while res_size
上一篇:Java发展历程摘要