scoket模块 粘包问题 tcp协议特点 重启服务器会遇到地址被占用问题
scoket()模块函数用法
import socket
socket.socket(socket_family,socket_type,protocal=0) 获取tcp/ip套接字
tcpsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 获取udp/ip套接字
udpsock =socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始tcp监听
s.accept() 被动接受tcp客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化tcp服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收tcp数据
s.send() 发送tcp数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.recvfrom() 接收udp数据
s.sendto() 发送udp数据
s.close() 关闭套接字
1 用打电话的流程快速描述socket通信
2 服务端和客户端加上基于一次连接的循环通信
3 客户端发送发空,卡主,证明是从那一个位置卡的 服务端:
from socket import * phone =socket(AF_INET,SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5) conn,addr =phone.accept()
while True:
data =conn.recv(1024)
print('server===>')
print(data)
conn.send(data.upper())
conn.close()
phone.close() 客户端:
from socket import * phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8081)) while True:
msg=input('>>: ').strip()
phone.send(msg.encode('utf-8'))
print('client===>')
data=phone.recv(1024)
print(data) 说明卡的原有:缓冲区为空recv就卡主,引出原理图 简单演示udp
服务端
from socket import *
phone =socket(AF_INET,SOCK_DGRAM)
phone.bind(('127.0.0.1',8082)) while True:
msg,addr =phone.recvfrom(1024)
phone.sendto(msg.upper(),addr) # 客户端
from socket import *
phone =socket(AF_INET,SOCK_DGRAM)
while True:
msg=input('>>: ')
phone.sendto(msg.encode('utf-8'),('127.0.0.1',8082))
msg,addr =phone.recvfrom(1024)
print(msg) udp客户端可以并发演示
udp客户端可以输入为空演示
socket 实验推演流程
基于TCP的套接字
tcp是基于连接的,必须先启动服务端,然后再启动客户端去连接服务端
tcp服务端
from socket import *
ss=socket() #创建服务器套接字
ss.bind() #把地址绑定到套接字
ss.listen() #监听连接
inf_loop: #服务器无线循环
cs=ss.accept() #接收客户端连接
comm_loop: #通信循环
cs.recv()/cs.send() #对话(接收与发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器套接字(可选)
tcp客户端
cs=socket() #创建客户套接字
cs.connect() #尝试连接服务器
comm_loop: #通讯循环
cs.send()/cs.recv() #对话(发送/接收)
cs.close() #关闭客户套接字
socket 通信流程与打电话流程类似
基于tcp协议通信的套接字
import socket server = socket.socket() #买手机
server.bind(('127.0.0.1',8080)) #插手机卡 bind((ip,port)) server.listen(5) #开机 半整数池 conn , addr =server.accept() #待机等待接电话
print(conn) #双向通道
print(addr) #地址 ('127.0.0.1', 52942) data = conn.recv(1024) #接听别人说话 只接收1024个字节 bytes print(data) #b'hello how much?'
conn.send(b'hello big baby!') #跟别人说话 conn.close() #关闭通信连接
server.close() #关闭服务端
服务端
import socket #t套接字对象
client = socket.socket() client.connect(('127.0.0.1',8080)) #找服务器 拨电话 client.send(b'hello how much?') #给服务器发信息
data = client.recv(1024) #接收服务器发来的信息
print(data) client.close() #关闭客户端
客户端
加上连接循环与通信循环
import socket
ip_port=('127.0.0.1',8081)#电话卡
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(ip_port) #手机插卡
s.listen(5) #手机待机 while True: #新增接收链接循环,可以不停的接电话
conn,addr=s.accept() #手机接电话
# print(conn)
# print(addr)
print('接到来自%s的电话' %addr[0])
while True: #新增通信循环,可以不断的通信,收发消息
msg=conn.recv(BUFSIZE) #听消息,听话 # if len(msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机
循环服务端
import socket
ip_port=('127.0.0.1',8081)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话 while True: #新增通信循环,客户端可以不断发收消息
msg=input('>>: ').strip()
if len(msg) == 0:continue
s.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话
print(feedback.decode('utf-8')) s.close() #挂电话
循环客户端
重启服务器时可能会遇到
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 或者 OSError:[Errno 48] Address already in use 这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(1 tcp三次握手,四次挥手 2 syn洪水攻击
3 服务器高并发情况下会有大量的time_wait状态的优化方法) #客户端没请求到服务器了 服务器要看一下自己是不会还有消息没法客户端 解放方法:
加入一条socket配置,重用ip和端口
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #在bind前面加 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
vi /etc/sysctl.conf 编辑文件,加入以下内容:
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 让参数生效。 net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间 方法二
方法二
tcp协议特点
import socket server = socket.socket()
server.bind(('127.0.0.1',8808)) server.listen(5) #半连接池 等待连接的最大的客户端数 conn,addr = server.accept() # data = conn.recv(1024)
# print(data)
#b'hellohellohello' 接收的小于1024的 所有的客户端发来的信息 data = conn.recv(4)
print(data)
#b'hell' data = conn.recv(5)
print(data)
#b'ohell' #会先接收上次没接收完的信息 data = conn.recv(5)
print(data)
#b'ohell' #会先接收上次没接收完的信息
conn.close() server.close()
服务端
import socket client = socket.socket() client.connect(('127.0.0.1',8808)) client.send(b'hello')
client.send(b'hello')
client.send(b'hello')
#tcp协议的优化特点 发送数据量小的间隔时间短 会一起发送
#会将数据量比较小的并且时间间隔比较短的数据一次性打包发送给接收端 client.close()
客户端
struct模块
import struct data='sdkjfksdfjsdkfjksdjfkjdsk' #服务端
res = struct.pack('i',len(data)) print(len(res)) #4 会把多个字符的字符串 打包成只有4个的对象 print(res) #b'\x19\x00\x00\x00' #客户端
ret=struct.unpack('i',res) #解包成原来的 字符串个数 i 代表int 整形
print(ret) #(25,)
print(ret[0]) #
模拟ssh实现远程执行命令
import socket
import subprocess
import struct
import json server =socket.socket()
server.bind(('127.0.0.1',8081))
server.listen(5) #半连接池 while True:
conn ,addr =server.accept() #等待电话
while True:
try:
data = conn.recv(1024).decode('utf-8')
if len(data) == 0:break
obj = subprocess.Popen(data,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
print(len(stderr+stdout)) header_dic = {
'filename':'cls.av',
'len':len(stdout+stderr)
} header_bytes = json.dumps(header_dic).encode('utf-8') #制作报头
header = struct.pack('i',len(header_bytes)) conn.send(header)
conn.send(header_bytes) conn.send(stdout)
conn.send(stderr)
#conn.send(stdout+stderr) 字符串拼接 从新拷贝一份占内存
# 和单独发也是一样的 间隔时间短的接收到的也是一起的
except ConnectionResetError:
break conn.close()
server.close()
服务端
import socket,struct,json client = socket.socket() client.connect(('127.0.0.1',8081)) while True:
msg = input('>>>: ').encode('utf-8')
if len(msg)==0:continue client.send(msg) header = client.recv(4) #接收字典打包后的4个长度的字节 精确接收
print(header,'这是报头的长度') head_len = struct.unpack('i',header)[0] #对这个头进行解包,获取原来的字典的字节数长度
print(head_len,'这是字典的字节数') head_dic =json.loads(client.recv(head_len).decode('utf-8'))
# 反序列化 接收字典的长度(精确接收)解码
#得到字典
print(head_dic,'这是字典')
#对需要接受的数据 进行循环接收
total_size = head_dic['len']
recv_size=0
res=b''
while recv_size < total_size: #字符
data = client.recv(1024) #每次接收到的信息
res +=data
recv_size +=len(data)
print(res.decode('gbk')) client.close()
客户端
udp套接字
udp是无连接的,先启动那一端都不会报错
udp服务端
ss=socket() #创建一个服务器的套接字
ss.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs=ss.recvfrom()/ss.sendto() #对话(接收与发送)
ss.close() #关闭服务器套接字
udp客户端
cs=socket() #创建客户端套接字
comm_loop: #通信循环
cs.sendto()/cs.recvfrom() #对话(发送/接收)
cs.close() #关闭客户套接字
udp套接字简单实例
服务端
import socket server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080)) while True:
data ,addr =server.recvfrom(1024)
print(data) server.sendto(data.upper(),addr)
客户端
import socket client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server_addr=('127.0.0.1',8080) #服务端地址,通常写在配置文件中 client.sendto(b'hello',server_addr)
udp 实现简易版本的qq
import socket
server=socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080)) while True:
msg,addr =server.recvfrom(1024)
print(addr,msg.decode('utf-8'))
data=input('服务端回复:').encode('utf-8')
server.sendto(data,addr)
服务端
import socket client =socket.socket(type=socket.SOCK_DGRAM) server_addr =('127.0.0.1',8080) while True:
msg = input('输入消息:')
msg = '客服端1的消息:%s' % msg
client.sendto(msg.encode('utf-8'),server_addr)
data,addr =client.recvfrom(1024)
print(data.decode('utf-8'))
客户端
import socket client =socket.socket(type=socket.SOCK_DGRAM) server_addr =('127.0.0.1',8080) while True:
msg = input('输入消息:')
msg = '客服端2的消息:%s' % msg
client.sendto(msg.encode('utf-8'),server_addr)
data,addr =client.recvfrom(1024)
print(data.decode('utf-8'))
客户端2
import socket client =socket.socket(type=socket.SOCK_DGRAM) server_addr =('127.0.0.1',8080) while True:
msg=input('输入消息:')
msg='客服端3的消息:%s'%msg
client.sendto(msg.encode('utf-8'),server_addr)
data,addr =client.recvfrom(1024)
print(data.decode('utf-8'))
客户端3
subprocess模块
import socket
import subprocess
import struct '''
服务端必备三要素:
1 要有固定的ip和port
2 要能24小时提供服务
3 能承受高并发
并发:看上去像同时进行
'''
server=socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5) while True:
conn,addr =server.accept()
while True:
try:
cmd =conn.recv(1024).decode('utf-8')
if len(cmd)==0:break #针对linux mac系统
obj=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout=obj.stdout.read()
stderr=obj.stderr.read() #制作固定报头
header=struct.pack('i',len(stderr+stdout))
#发送固定长度的报头
conn.send(header) #发送真是数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
服务端
import socket
import struct client =socket.socket()
client.connect(('127.0.0.1',8080)) while True:
cmd =input('>>:').encode('utf-8')
if len(cmd)==0:continue
client.send(cmd) header=client.recv(4)
#解析报头获取真是数据的长度
total_size=struct.unpack('i',header)[0]
recv_size=0
data=b""
while recv_size<total_size:
res=client.recv(1024)
recv_size+=len(res)
data+=res print(data.decode('gbk'))
客户端
socketserver模块 实现并发 基于tcp的套接字,关键字就是两个循环,一个是连接循环,一个通信循环
socketserver模块中分两大类:server类(解决连接问题)和request类(解决通信问题)
基于tcp的socketserver自定义的类中的
1 self.server即套接字对象
2 self.request即一个连接
3 self.client_address即客户端地址
基于udp的socketserver自定义的类中的
1 self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),
如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
2 self.client_address即客户端地址
import socket client =socket.socket()
client.connect(('127.0.0.1',8080)) while True:
client.send(b'hello world')
data= client.recv(1024) print(data)
tcp客户端
import socketserver class MyBaby(socketserver.BaseRequestHandler):
def handle(self):
#通信循环
while True:
#self.request相当于你的conn通信对象
data= self.request.recv(1024) #收消息
print(data)
self.request.send(data.upper()) if __name__ == '__main__': #通道循环
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyBaby)
server.serve_forever()
tcp服务端
import socket
import time client =socket.socket(type=socket.SOCK_DGRAM)
server_addr=('127.0.0.1',8080) while True:
client.sendto(b'hello world',server_addr)
data,addr =client.recvfrom(1024)
print(data,addr)
time.sleep(1)
udp客户端
import socketserver class MyBaby(socketserver.BaseRequestHandler):
def handle(self):
#通讯循环
while True:
#self.request 相当于conn通讯对象
data,sock =self.request #收消息
print(data)
print(sock)
sock.sendto(data.upper(),self.client_address) #self.client_address 客户端地址 if __name__ == '__main__':
server=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyBaby)
server.serve_forever()
udp服务端
基于tcp文件上传
import socket
import struct
import json server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5) while True:
conn, addr = server.accept() while True:
try:
# 接收报头
header = conn.recv(4)
# 解析报头获取字典长度
header_len = struct.unpack('i', header)[0] # 接收字典
header_bytes = conn.recv(header_len)
header_dic = json.loads(header_bytes.decode('utf-8'))
print(header_dic) # 循环接收文件存储到本地
file_size = header_dic.get('file_size')
file_name = header_dic.get('file_name') recv_size = 0
# 文件操作
with open(file_name, 'wb')as f:
# 循环接收文件
while recv_size < file_size:
data = conn.recv(1024)
f.write(data)
recv_size += len(data)
print(header_dic.get('msg'))
except ConnectionResetError:
break
conn.close()
服务端
import socket
import os
import json
import struct client = socket.socket()
client.connect(('127.0.0.1', 8080)) # 文件大小
file_size = os.path.getsize(r'E:\代码存放位置\第八周\莫文蔚-偷情.mp3') # 文件名字
file_name = '歌曲名:偷情- 歌手:莫文蔚.mp3' # 定义一个字典
d = {
"file_name": file_name,
"file_size": file_size,
"msg": '注意身体哦' } data_bytes = json.dumps(d).encode('utf-8') # 制作字典的报头
header = struct.pack('i', len(data_bytes)) # 发送报头
client.send(header) # 发送字典
client.send(data_bytes) # 发送真实数据
with open(r'E:\代码存放位置\第八周\莫文蔚-偷情.mp3', 'rb')as f:
for line in f:
client.send(line)
客户端
import json res = {'name': '他说大帅比'}
# print(json.dumps(res,ensure_ascii=False)) #{"name": "他说大帅比"}
print(json.dumps(res)) # {"name": "\u4ed6\u8bf4\u5927\u5e05\u6bd4"} ''' TCP
三次握手
可靠协议,流式协议 粘包问题的产生:
1.接收方:
我不知道我要接受的数据的总长度
2.发送方:
由于TCP协议的内部优化算法negle
1.会将数据量比较的小的并且时间间隔比较短的数据一次性打包发送 UDP
数据报协议
没有双向通道 1.UDP协议不存在粘包问题
2.客户端可以发空
3.udp可以实现并发的效果
4.服务端不存在,也不影响客户端朝服务端发送数据 TCP/UDP
TCP:打电话
UDP:发短信 SocketServer模块
1.能够实现并发效果
并发:看起来像同时运行就能称之位并发 2.udp在使用的时候,多个客户端要有一些io操作
不然容易卡死 并发编程
操作系统发展史 多道技术:
- 空间上的复用(多个程序共一套硬件设备,它是多道技术实现时间上的复用的基础,
不然还要去硬盘读数据) - 时间上的复用(单个cpu的电脑上,起多个应用程序。cpu快速切换,给人的感觉是同时运行) cpu两种情况下才会切换:(先保存当前程序的运行状态)
- 一个任务占用cpu时间过长或被操作系统强行剥夺走cpu的执行权限(比起串行效率反而降低)
- 一个任务执行过程中遇到io操作,也会被操作系统强行剥夺走cpu的执行权限(比起串行效率提高) 并发:看上去像同时进行的
并行:同时运行
补充:单核的计算机不可能实现并行!
'''
scoket模块 粘包问题 tcp协议特点 重启服务器会遇到地址被占用问题的更多相关文章
- python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议
socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...
- 网络编程 --- subprocess模块,struct模块,粘包,UDP协议,socket_server模块
目录 subprocess模块 struct模块 粘包 UDP协议 socket_server模块 subprocess模块 作用: 1.可以帮你通过代码执行操作系统的终端命令 2.并返回终端执行命令 ...
- (day27)subprocess模块+粘包问题+struct模块+ UDP协议+socketserver
目录 昨日回顾 软件开发架构 C/S架构 B/S架构 网络编程 互联网协议 socket套接字 今日内容 一.subprocess模块 二.粘包问题 三.struct模块 四.UDP 五.QQ聊天室 ...
- (网络编程)基于tcp(粘包问题) udp协议的套接字通信
import socket 1.通信套接字(1人1句)服务端和1个客户端 2.通信循环(1人多句)服务端和1个客户端 3.通信循环(多人(串行)多句)多个客户端(服务端服务死:1个客户端---&g ...
- 【转】Netty之解决TCP粘包拆包(自定义协议)
1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...
- Netty之解决TCP粘包拆包(自定义协议)
1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...
- socket模块粘包现象理解以及解决思路
粘包现象: 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的 ...
- socker通信-struct模块-粘包问题
Socket概念 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对 ...
- Wireshark抓包分析TCP协议
版权声明:本文为作者原创文章,可以随意转载,但必须在明确位置表明出处!!! 之前有一篇文章介绍了http协议「初识http协议」, http协议协议是基于tcp协议的,所以作者觉得有必要针对tcp ...
随机推荐
- 【MAC工具】手机抓包工具之 —— whistle
本文链接:https://blog.csdn.net/weixin_42534940/article/details/88783455安装参考: https://github.com/avwo/whi ...
- 死磕Java之聊聊HashMap源码(基于JDK1.8)
死磕Java之聊聊HashMap源码(基于JDK1.8) http://cmsblogs.com/?p=4731 为什么面试要问hashmap 的原理
- Linux 网络通信命令之 netstat
定义 Linux netstat命令用于显示网络状态. 利用netstat指令可让你得知整个Linux系统的网络情况. 语法 netstat [-acCeFghilMnNoprstuvVwx][-A& ...
- mybatis 语句中where 后边要跟必要条件和多个选择条件处理方法
<select id="serchRelation" resultType="Relation">SELECTr.node_one as nodeO ...
- 用单元测试来调试SilverFish AI
[TestFixture] public class AiTest { [Test] public void Test() { Settings.Instance.LogFolderPath = @& ...
- 提高组刷题营 DAY 1 下午
DFS 深度优先搜索 通过搜索得到一棵树形图 策略:只要能发现没走过的点,就走到它.有多个点可走就随便挑一个,如果无路可走就回退,再看有没有没走过的点可走. 在图上寻找路径[少数可用最短路解决]:最短 ...
- P3146 [USACO16OPEN]248
P3146 [USACO16OPEN]248 题解 第一道自己码出的区间DP快庆祝一哈 2048 每次可以合并任意相邻的两个数字,得到的不是翻倍而是+1 dp[L][R] 区间 L~R 合并结果 然后 ...
- LaTeX统计文章字数
今天学会了用LaTeX命令统计文章字数,命令如下: textcount doc_full_path.tex 效果如下:
- WEditor使用方法
APP的定位方式有: 1. Appium Desktop工具里的Inspector 2. /tools/bin/uiautomatorviewer.bat 最近发现在一个更好用的定位工具: Wedit ...
- 连接服务器VNC
1,启动vnc vncserver 2,提示输入密码 3,Would you like to enter a view-only password (y/n)? 选择n 4,会生成一个端口号 5, ...