8、socket以及socketserver
一 网络基础
1. 因此ip地址精确到具体的一台电脑,而端口精确到具体的程序。
2. iosi七层模型
互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层
每层常见物理设备
每层常见协议
二 socekt
1. tcp/udp协议
TCP(Transmission Control Protocol)可靠的、面向连接的协议、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
现在Internet上流行的协议是TCP/IP协议,该协议中对低于1024的端口都有确切的定义,他们对应着Internet上一些常见的服务。这些常见的服务可以分为使用TCP端口(面向连接)和使用UDP端口(面向无连接)两种。端口可用的为1025-65535
2. 基于tcp协议的socket
2.1 server端
import socket
sk = socket.socket() # 实例化
sk.bind(('localhost', 8898)) # 把地址绑定到套接字,元组类型,回环地址
sk.listen() # 监听链接
conn, addr = sk.accept() # 接受客户端链接,accept()返回为: return sock, addr
ret = conn.recv(1024) # 接收客户端信息
print(ret) # 打印客户端信息
conn.send(b'hi') # 向客户端发送信息,必须是bytes类型
conn.close() # 关闭客户端套接字
sk.close() # 关闭服务器套接字
2.2 client端
import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('localhost', 8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字
2.3 服务端重启问题解决
服务器端重启的时候若报错类型为:OSError
# 加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET, SO_REUSEADDR #允许端口重用,开启多个server端,实际中少用
sk = socket.socket()
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) #此句,在bind前加
sk.bind(('localhost', 8898)) # 把地址绑定到套接字
sk.listen() # 监听链接
conn, addr = sk.accept() # 接受客户端链接
ret = conn.recv(1024) # 接收客户端信息
print(ret) # 打印客户端信息
conn.send(b'hi') # 向客户端发送信息
conn.close() # 关闭客户端套接字
sk.close() # 关闭服务器套接字(可选)
2.4 socket模块方法
import socket
socket.socket(socket_family,socket_type,protocal=0)
socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 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.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() 创建一个与该套接字相关的文件
3. 基于UDP协议的socket
3.1 server端
import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) # 创建一个服务器的套接字
udp_sk.bind(('127.0.0.1', 9000)) # 绑定服务器套接字
msg, addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi', addr) # 对话(接收与发送)
udp_sk.close() # 关闭服务器套接字
3.2 client端
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
4. subprocess模块
4.1 参数说明:
该模块能够创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。 主要应用该模块中的Popen类实现远程命令。
#Popen的初始化函数及参数说明:
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True,
shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, encoding=None, errors=None, text=None):
args:args should be a string, or a sequence of program arguments.用于指定进程的可执行文件及其参数。如果是一个序列类型参数,则序列的第一个元素通常都必须是一个可执行文件的路径。当然也可以使用executeable参数来指定可执行文件的路径。
stdin,stdout,stderr:分别表示程序的标准输入、标准输出、标准错误。有效的值可以是PIPE,存在的文件描述符,存在的文件对象或None,如果为None需从父进程继承过来,stdout可以是PIPE,表示对子进程创建一个管道,stderr可以是STDOUT,表示标准错误数据应该从应用程序中捕获并作为标准输出流stdout的文件句柄。
shell:如果这个参数被设置为True,程序将通过shell来执行。
env:它描述的是子进程的环境变量。如果为None,子进程的环境变量将从父进程继承而来。
4.2 实例化
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
cmd:标准像子进程传入需要执行的shell命令,如:ls -al
subprocess.PIPE:在创建Popen对象时,subprocess.PIPE可以初始化为stdin, stdout或stderr的参数,表示与子进程通信的标准输入流,标准输出流以及标准错误。
subprocess.STDOUT:作为Popen对象的stderr的参数,表示将标准错误通过标准输出流输出。
4.3 方法和属性
1、Popen.pid()
获取子进程的进程ID。
2、Popen.returncode ()
获取进程的返回码。如果进程未结束,将返回None。
3、communicate(input=None)
与子进程进行交互,像stdin发送数据,并从stdout和stderr读出数据存在一个tuple中并返回。参数input应该是一个发送给子进程的字符串,如果未指定数据,将传入None。
4、poll()
检查子进程是否结束,并返回returncode属性。
5、wait()
等待子进程执行结束,并返回returncode属性,如果为0表示执行成功。
6、send_signal( sig)
发送信号给子进程。
7、terminate()
终止子进程。windows下将调用Windows API TerminateProcess()来结束子进程。
8、kill()
官方文档对这个函数的解释跟terminate()是一样的,表示杀死子进程。
4.4远程命令实例
os.popen()缺点,发生错误,不能讲错误信息返回,所以是有subprocess.Popen()方法。
# server端
import socket
import subprocess
sk = socket.socket()
sk.bind(('localhost', 5456))
sk.listen(5)
while 1:
conn, addr = sk.accept()
while 1:
cmds = conn.recv(1024)
if cmds.decode() == 'q':break
res = subprocess.Popen(cmds.decode(), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if res.stdout:
conn.send(res.stdout.read()) # read()返回的是GBK的bytes类型
else:
conn.send(res.stderr.read()) # 未报错返回None,Noen没有read()方法,直接运行报错
conn.close()
客户端为:
#client端
import socket
sk = socket.socket()
sk.connect(('localhost', 5456))
while 1:
cmds = input('>>>>') #输入的数据不能输字符串,出错
if not cmds:
continue
sk.send(cmds.encode())
data = sk.recv(102400)
print(data.decode('gbk'))
三 黏包
1. 黏包成因
- 发送端:TCP协议采用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据合并,然后进行封包一起发送,接收端就难于分辨。
- 接收端:接受端接受数据过小或接受不及时,导致发送的数据在接收端的缓存区内堆积,造成黏包。
UDP不采用合并优化算法,接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),对于接收端来说,就容易进行区分处理了。对于空消息:tcp是基于数据流的,收发的消息为空会使程序卡住;而udp是基于数据报的,输入的空内容udp会封装上消息头发送过去。 总结如下:
- 黏包只会在tcp中产生(发送端和接收端两种原因),udp中不会
- tcp不能发送空消息,而udp可以发送空消息
拓展:当发送端缓冲区的长度大于网卡的MTU时,tcp、udp会将这次发送的数据拆成几个数据包发送出去。 MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。UDP包大小有上限,即64K。
2. stuct 解决黏包
2.1 struct模块
该模块可以把一个类型,如数字,转成固定长度的bytes
import struct
ret = struct.pack('i', 183346) # 将一个数字转化成等长度的bytes类型。
print(ret, type(ret), len(ret)) ret1 = struct.unpack('i', ret)[0] # 通过unpack反解回来,整体以元组返回(183346,)
print(ret1, type(ret1)) #ret = struct.pack('l', 4323241232132324) # 但是通过struct 处理不能处理太大
#print(ret, type(ret), len(ret)) # 报错
b'2\xcc\x02\x00' <class 'bytes'> 4;183346 <class 'int'>
具体参数含义如下:
2.2 定制报头形式
网络上传输的所有数据都叫数据包,里面的数据叫做报文,报文里面不止有你的数据,还有ip地址、mac地址、端口号等等,所有的报文都有报头,这个报头是协议规定的。我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节。
发送时:
- 先发报头长度
- 再编码报头内容
- 然后发送最后发真实内容
接收时:
- 先接受报头长度,用struct取出来
- 根据取出的长度收取报头内容,然后解码,反序列化
- 从反序列化的结果中取出待取数据的描述信息,然后去取真实的数据内容
# server端
import socket, json, struct
import subprocess
sk = socket.socket()
sk.bind(('localhost', 5456))
sk.listen(5)
while 1:
conn, addr = sk.accept()
while 1:
cmds = conn.recv(1024)
if cmds.decode() == 'q': break
res = subprocess.Popen(cmds.decode(), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
ret = res.stdout.read() if res.stdout else res.stderr.read()
header_dict = {'data_size': len(ret)}
header_bytes = json.dumps(header_dict).encode() conn.send(struct.pack('i',len(header_bytes))) #发送4位报头大小
conn.send(header_bytes) #发送报头
conn.send(ret) #发送数据
conn.close()
客户端:
#client端
import socket,json,struct
sk = socket.socket()
sk.connect(('localhost', 5456))
while 1:
cmds = input('>>>>')
if not cmds:continue
sk.send(cmds.encode()) header_size = sk.recv(4)
header_size = struct.unpack('i',header_size)[0] #报头长度
head_dict = json.loads(sk.recv(header_size).decode()) #报头
data_size = head_dict['data_size'] recv_size = 0
recv_data = b''
while recv_size < data_size:
recv_data += sk.recv(1024)
recv_size += len(recv_data)
print(recv_data.decode('gbk'))
四 socketserver
主要解决TCP中server不能同时连接多个client的问题,只修改server,不用修改client
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self): # handle名称固定,逻辑代码写在该类下
self.data = self.request.recv(1024).decode() #self.request = conn
print(self.data)
self.request.sendall(self.data.upper().encode())
server = socketserver.TCPServer(('localhost', 5456), Myserver)
server.serve_forever() # 开启永久服务
8、socket以及socketserver的更多相关文章
- Socket与SocketServer结合多线程实现多客户端与服务器通信
需求说明:实现多客户端用户登录,实现多客户端登录一般都需要使用线程技术: (1)创建服务器端线程类,run()方法中实现对一个请求的响应处理: (2)修改服务器端代码,实现循环监听状态: (3)服务器 ...
- python_way day10 python和其他语言的作用域 、 python2.7多继承和3.5多继承的区别 、 socket 和 socketserver源码(支持并发处理socket,多进程,多线程)
python_way day10 1.python的作用域和其他语言的作用域 2.python2.7多继承和3.5多继承的区别 3.socket和socketserver源码(并发处理socket) ...
- Python进阶----UDP协议使用socket通信,socketserver模块实现并发
Python进阶----UDP协议使用socket通信,socketserver模块实现并发 一丶基于UDP协议的socket 实现UDP协议传输数据 代码如下:
- 自己实现多线程的socket,socketserver源码剖析
1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是:1,select和poll所有平台都支持,epoll只有linux支持2 ...
- python socket和socketserver
Python提供了两个基本的socket模块.一个是socket,它提供了标准的BSD Socket API:另一个是socketServer,它提供了服务器中心类,可以简化网络服务器的开发. 下面先 ...
- 10.python之socket和socketserver
1.socket介绍 socket的英文原义是"孔"或"插座".作为BSD UNIX的进程通信机制,取后一种意思.通常也 称作"套接字",用 ...
- socket 和 SocketServer 模块
一 .Socket 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket socket(TCP,IP)通常也称作"套接字",用于描述IP地址和端 ...
- Python自动化运维之15、网络编程之socket、socketserver、select、twisted
一.TCP/IP相关知识 TCP/UDP提供进程地址,两个协议互不干扰的独自的协议 TCP :Transmission Control Protocol 传输控制协议,面向连接的协议,通信 ...
- python成长之路9——socket和socketserver
IPC:进程间通信 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具 ...
随机推荐
- 分布式版本控制系统 Git 的安装与使用
作业的要求来自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2097 远端库地址:https://github.com/CJL29 ...
- linux通过expect工具来实现自动登录服务器,并执行相关操作
参考地址:https://www.cnblogs.com/liyuanhong/articles/7728034.html EOF的使用参考:https://www.cnblogs.com/liyua ...
- js根据毫米/厘米算像素px
<html><meta http-equiv="content-type" content="text/html;charset=utf-8" ...
- 【转载】VS写汇编程序01:VS2015配置汇编语言开发环境
https://blog.csdn.net/qq_28249373/article/details/83475107
- Jupyter Notebook中的快捷键
1.快捷键 Jupyter Notebook 有两种键盘输入模式.编辑模式,允许你往单元中键入代码或文本:这时的单元框线是绿色的.命令模式,键盘输入运行程序命令:这时的单元框线是灰色. 命令模式 (按 ...
- windows 双网卡同时上专网(内网)和外网
本操作是用网线做专网(内网),无线网卡用于外网 1. 记录有线网卡的网络的网关,例如10.103.14.1 2. 有线网卡必须是手动指定的ip地址,把网关清掉,例如 3. 删除0.0.0.0 路由 r ...
- redis基础篇
1.redis常见的数据结构 redis是一种以键值对存储的高性能内存数据库,有五种常用的数据类型,string,list,hash,set,zset. 2.redis的过期时间 redis中的key ...
- golang interface类型转string等其他类型
inter 是interface类型,转化为string类型是: str := inter .(string) 转为其他类型也类似
- webpack2 实践
实例gif图: 目录截图: 目录介绍: dist目录(最后生成的目录,里面文件为配置webpack自动生成的): c/:css文件夹; i/:img文件夹; j/:js文件夹; src目录下(开发目录 ...
- npm vue ivew vue-cli3
2019-4-10 10:56:20 星期三 学习iview时需要搭建一套node环境, 这里记录下来 1. 下载安装nodejs //自带了npm包管理器 2. 设置npm的全局配置: 全局包默认 ...