socketserver模块实现并发

  为什么要讲socketserver?我们之前写的tcp协议的socket是不是一次只能和一个客户端通信,如果用socketserver可以实现和多个客户端通信。它是在socket的基础上进行了一层封装,也就是说底层还是调用的socket,在py2.7里面叫做SocketServer也就是大写了两个S,在py3里面就小写了。后面我们要写的FTP作业,需要用它来实现并发,也就是同时可以和多个客户端进行通信,多个人可以同时进行上传下载等。
 
  那么我们先看socketserver怎么用呢,然后在分析,先看下面的代码
  
import socketserver                              #1、引入模块
class MyServer(socketserver.BaseRequestHandler): #2、自己写一个类,类名自己随便定义,然后继承socketserver这个模块里面的BaseRequestHandler这个类 def handle(self): #3、写一个handle方法,必须叫这个名字
#self.request #6、self.request 相当于一个conn self.request.recv(1024) #7、收消息
msg = '亲,学会了吗'
self.request.send(bytes(msg,encoding='utf-8')) #8、发消息 self.request.close() #9、关闭连接
# 拿到了我们对每个客户端的管道,那么我们自己在这个方法里面的就写我们接收消息发送消息的逻辑就可以了
pass
if __name__ == '__mian__':
#thread 线程,现在只需要简单理解线程,别着急,后面很快就会讲到啦,看下面的图
server = socketserver.ThreadingTCPServer(('127.0.0.1',8090),MyServer)#4、使用socketserver的ThreadingTCPServer这个类,将IP和端口的元祖传进去,还需要将上面咱们自己定义的类传进去,得到一个对象,相当于我们通过它进行了bind、listen
server.serve_forever() #5、使用我们上面这个类的对象来执行serve_forever()方法,他的作用就是说,我的服务一直开启着,就像京东一样,不能关闭网站,对吧,并且serve_forever()帮我们进行了accept #注意:
#有socketserver 那么有socketclient的吗?
#当然不会有,我要作为客户去访问京东的时候,京东帮我也客户端了吗,客户端是不是在我们自己的电脑啊,并且socketserver对客户端没有太高的要求,只需要自己写一些socket就行了。

 

  ThreadingTCPServer,多线程,简单解释:看图
  

  通过上面的代码,我们来分析socket的源码:(大家还记得面向对象的继承吗,来,实战的时候来啦)

在整个socketserver这个模块中,其实就干了两件事情:1、一个是循环建立链接的部分,每个客户链接都可以连接成功  2、一个通讯循环的部分,就是每个客户端链接成功之后,要循环的和客户端进行通信。
看代码中的:server=socketserver.ThreadingTCPServer(('127.0.0.1',8090),MyServer)

还记得面向对象的继承吗?来,大家自己尝试着看看源码:
查找属性的顺序:ThreadingTCPServer->ThreadingMixIn->TCPServer->BaseServer 实例化得到server,先找ThreadMinxIn中的__init__方法,发现没有init方法,然后找类ThreadingTCPServer的__init__,在TCPServer中找到,在里面创建了socket对象,进而执行server_bind(相当于bind),server_active(点进去看执行了listen)
找server下的serve_forever,在BaseServer中找到,进而执行self._handle_request_noblock(),该方法同样是在BaseServer中
执行self._handle_request_noblock()进而执行request, client_address = self.get_request()(就是TCPServer中的self.socket.accept()),然后执行self.process_request(request, client_address)
在ThreadingMixIn中找到process_request,开启多线程应对并发,进而执行process_request_thread,执行self.finish_request(request, client_address)
上述四部分完成了链接循环,本部分开始进入处理通讯部分,在BaseServer中找到finish_request,触发我们自己定义的类的实例化,去找__init__方法,而我们自己定义的类没有该方法,则去它的父类也就是BaseRequestHandler中找....
源码分析总结: 基于tcp的socketserver我们自己定义的类中的   self.server即套接字对象
  self.request即一个链接
  self.client_address即客户端地址
基于udp的socketserver我们自己定义的类中的   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)>)
  self.client_address即客户端地址
 
 
  一个完整的sockeserver代码示例:
    服务端代码示例:
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
self.request.sendall(self.data.upper()) if __name__ == "__main__":
HOST, PORT = "127.0.0.1", 9999 # 设置allow_reuse_address允许服务器重用地址
socketserver.TCPServer.allow_reuse_address = True
# 创建一个server, 将服务地址绑定到127.0.0.1:9999
#server = socketserver.TCPServer((HOST, PORT),Myserver)
server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver)
# 让server永远运行下去,除非强制停止程序
server.serve_forever() tcp_server.py

     客户端代码示例:

import socket

HOST, PORT = "127.0.0.1", 9999
data = "hello" # 创建一个socket链接,SOCK_STREAM代表使用TCP协议
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT)) # 链接到客户端
sock.sendall(bytes(data + "\n", "utf-8")) # 向服务端发送数据
received = str(sock.recv(1024), "utf-8")# 从服务端接收数据 print("Sent: {}".format(data))
print("Received: {}".format(received)) tcp_client.py

FTP上传下载文件的代码(简易版)

import socket
import struct
import json
sk = socket.socket()
# buffer = 4096 # 当双方的这个接收发送的大小比较大的时候,就像这个4096,就会丢数据,这个等我查一下再告诉大家,改小了就ok的,在linux上也是ok的。
buffer = 1024 #每次接收数据的大小
sk.bind(('127.0.0.1',8090))
sk.listen() conn,addr = sk.accept()
#接收
head_len = conn.recv(4)
head_len = struct.unpack('i',head_len)[0] #解包
json_head = conn.recv(head_len).decode('utf-8') #反序列化
head = json.loads(json_head)
filesize = head['filesize']
with open(head['filename'],'wb') as f:
while filesize:
if filesize >= buffer: #>=是因为如果刚好等于的情况出现也是可以的。
content = conn.recv(buffer)
f.write(content)
filesize -= buffer
else:
content = conn.recv(buffer)
f.write(content)
break conn.close()
sk.close() tcp_server.py
import os
import json
import socket
import struct
sk = socket.socket()
sk.connect(('127.0.0.1',8090))
buffer = 1024 #读取文件的时候,每次读取的大小
head = {
'filepath':r'D:\打包程序', #需要下载的文件路径,也就是文件所在的文件夹
'filename':'xxx.mp4', #改成上面filepath下的一个文件
'filesize':None,
} file_path = os.path.join(head['filepath'],head['filename'])
filesize = os.path.getsize(file_path)
head['filesize'] = filesize
# json_head = json.dumps(head,ensure_ascii=False) #字典转换成字符串
json_head = json.dumps(head) #字典转换成字符串
bytes_head = json_head.encode('utf-8') #字符串转换成bytes类型
print(json_head)
print(bytes_head) #计算head的长度,因为接收端先接收我们自己定制的报头,对吧
head_len = len(bytes_head) #报头长度
pack_len = struct.pack('i',head_len)
print(head_len)
print(pack_len)
sk.send(pack_len) #先发送报头长度
sk.send(bytes_head) #再发送bytes类型的报头 #即便是视频文件,也是可以按行来读取的,也可以readline,也可以for循环,但是读取出来的数据大小就不固定了,影响效率,有可能读的比较小,也可能很大,像视频文件一般都是一行的二进制字节流。
#所有我们可以用read,设定一个一次读取内容的大小,一边读一边发,一边收一边写
with open(file_path,'rb') as f:
while filesize:
if filesize >= buffer: #>=是因为如果刚好等于的情况出现也是可以的。
content = f.read(buffer) #每次读取出来的内容
sk.send(content)
filesize -= buffer #每次减去读取的大小
else: #那么说明剩余的不够一次读取的大小了,那么只要把剩下的读取出来发送过去就行了
content = f.read(filesize)
sk.send(content)
break sk.close() tcp_client.py

  FTP上传下载文件的代码(升级版)(注:咱们学完网络编程就留FTP作业,这个代码可以参考,当你用函数的方式写完之后,再用面向对象进行改版却没有思路的时候再来看,别骗自己昂~~)

import socket
import struct
import json
import subprocess
import os class MYTCPServer:
address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding='utf-8' request_queue_size = 5 server_dir='file_upload' def __init__(self, server_address, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
self.server_address=server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise def server_bind(self):
"""Called by constructor to bind the socket.
"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname() def server_activate(self):
"""Called by constructor to activate the server.
"""
self.socket.listen(self.request_queue_size) def server_close(self):
"""Called to clean-up the server.
"""
self.socket.close() def get_request(self):
"""Get the request and client address from the socket.
"""
return self.socket.accept() def close_request(self, request):
"""Called to clean up an individual request."""
request.close() def run(self):
while True:
self.conn,self.client_addr=self.get_request()
print('from client ',self.client_addr)
while True:
try:
head_struct = self.conn.recv(4)
if not head_struct:break head_len = struct.unpack('i', head_struct)[0]
head_json = self.conn.recv(head_len).decode(self.coding)
head_dic = json.loads(head_json) print(head_dic)
#head_dic={'cmd':'put','filename':'a.txt','filesize':123123}
cmd=head_dic['cmd']
if hasattr(self,cmd):
func=getattr(self,cmd)
func(head_dic)
except Exception:
break def put(self,args):
file_path=os.path.normpath(os.path.join(
self.server_dir,
args['filename']
)) filesize=args['filesize']
recv_size=0
print('----->',file_path)
with open(file_path,'wb') as f:
while recv_size < filesize:
recv_data=self.conn.recv(self.max_packet_size)
f.write(recv_data)
recv_size+=len(recv_data)
print('recvsize:%s filesize:%s' %(recv_size,filesize)) tcpserver1=MYTCPServer(('127.0.0.1',8080)) tcpserver1.run() server.py
import socket
import struct
import json
import os class MYTCPClient:
address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding='utf-8' request_queue_size = 5 def __init__(self, server_address, connect=True):
self.server_address=server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if connect:
try:
self.client_connect()
except:
self.client_close()
raise def client_connect(self):
self.socket.connect(self.server_address) def client_close(self):
self.socket.close() def run(self):
while True:
inp=input(">>: ").strip()
if not inp:continue
l=inp.split()
cmd=l[0]
if hasattr(self,cmd):
func=getattr(self,cmd)
func(l) def put(self,args):
cmd=args[0]
filename=args[1]
if not os.path.isfile(filename):
print('file:%s is not exists' %filename)
return
else:
filesize=os.path.getsize(filename) head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
print(head_dic)
head_json=json.dumps(head_dic)
head_json_bytes=bytes(head_json,encoding=self.coding) head_struct=struct.pack('i',len(head_json_bytes))
self.socket.send(head_struct)
self.socket.send(head_json_bytes)
send_size=0
with open(filename,'rb') as f:
for line in f:
self.socket.send(line)
send_size+=len(line)
print(send_size)
else:
print('upload successful') client=MYTCPClient(('127.0.0.1',8080)) client.run() client.py

Python socketserver ftp功能简单讲解的更多相关文章

  1. python之socketserver ftp功能简单讲解

    TCP协议中的socket一次只能和一个客户端通信,然而socketserver可以实现和多个客户端通信. 它是在socket的基础上进行了一层封装,底层还是调用的socket # 服务端 impor ...

  2. day29 socketsever ftp功能简单讲解

    今日所学 一.ftp上传简单实例 二.socketsever的固定用法 三.验证合法性连接 1.ftp上传实例 这个题目是我们现在网络编程比较基础一点的题目 下面我们只写简单上传的代码 上传服务端的代 ...

  3. python socketserver ftp上传功能

    一.socketserver用于多个客户端访问同一个服务端 客户端 import socket client = socket.socket() ip_port = ('127.0.0.1',8001 ...

  4. python网络编程--socketserver 和 ftp功能简单说明

    1. socketserver 我们之前写的tcp协议的socket是不是一次只能和一个客户端通信,如果用socketserver可以实现和多个客户端通信.它是在socket的基础上进行了一层封装,也 ...

  5. day 29 socketserver ftp功能的简单讲解

    1.上传下载的简单示例 server: import socket import struct import json   server =socket.socket() server.bind((' ...

  6. python中的类简单讲解

    类似其它的语言, Python 中的函数使用小括号( () )调用.函数在调用之前必须先定义.如果函数中没有 return 语句, 就会自动返回 None 对象.      Python 是通过引用调 ...

  7. FTP的搭建与虚拟目录作用<之简单讲解>

    操作系统:win7 VS2010编写WebService与在IIS的发布<之简单讲解>中我已经说了IIS安装与使用,不明白的可以跳过去看. 1.添加FTP站点 2. 3. 4. 5. zq ...

  8. python实现FTP弱口令扫描器与简单端口扫描器

    python实现FTP弱口令扫描器与简单端口扫描器 目录 FTP弱口令扫描器 简单端口扫描器 参考: https://blog.csdn.net/rebelqsp/article/details/22 ...

  9. Python实现FTP服务功能

    本文从以下三个方面, 阐述Python如何搭建FTP服务器 一. Python搭建FTP服务器 二. FTP函数释义 三. 查看目录结构 四. 上传下载程序 一. Python搭建FTP服务器 1. ...

随机推荐

  1. windows 系统使用 git 和码云管理代码(本地已有项目)

    1. 为本地项目创建本地仓 找到项目所在的根目录(目录下有解决方案那个),右击目录,点击右键菜单中的“Git Bash Here”(前提是你的电脑已经装了Git,我用的是TortoiseGit) 然后 ...

  2. 【LeetCode】成对交换节点

    e.g. 给定链表 1->2->3->4,返回 2->1->4->3 的头节点. 我写了个常见的从头节点遍历,少量的奇数个或偶数个数据都能成功重新排列.但链表过长时 ...

  3. [洛谷 P3239] [HNOI2015]亚瑟王

    [HNOI2015]亚瑟王 题目描述 小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑.他决定,在脱坑之前,最后再来打一盘亚瑟王.既然是最后一战,就一定要打得漂亮.众所周知, ...

  4. openssl实现双向认证教程(服务端代码+客户端代码+证书生成)

    一.背景说明 1.1 面临问题 最近一份产品检测报告建议使用基于pki的认证方式,由于产品已实现https,商量之下认为其意思是使用双向认证以处理中间人形式攻击. <信息安全工程>中接触过 ...

  5. 牛客网 PAT 算法历年真题 1002 :数字分类 (20)

    1002 :数字分类 (20) 时间限制 1000 ms 内存限制 32768 KB 代码长度限制 100 KB 判断程序 Standard (来自 小小) 题目描述 给定一系列正整数,请按要求对数字 ...

  6. swagger访问api, TypeError: Failed to fetch

    用swagger访问https://localhost:44360/api/ads/1, 得到的结果是 TypeError: Failed to fetch.一开始以为是后端代码问题,检查了好久,才发 ...

  7. 谈一谈JUnit神奇的报错 java.lang.Exception:No tests found matching

    最近在学习Spring+SpringMVC+MyBatis,一个人的挖掘过程确实有点艰难,尤其是有一些神奇的报错让你会很蛋疼.特别是接触一些框架还是最新版本的时候,会因为版本问题出现很多错误,欢迎大家 ...

  8. php读取excel时间42930转化为时间然后正则验证时间是否通过

    excel时间 function exceltimtetophp($days,$time=false) { if(is_numeric($days)) { //凯撒日计数,要把我们运用的从1970年开 ...

  9. Win10系列:VC++绘制几何图形5

    打开D2DBasicAnimation.h头文件,并在D2DBasicAnimation类中添加如下的代码: private:     //声明成员变量point     D2D1_POINT_2F ...

  10. java倒计时使用java.util.Timer实现,使用两个线程,以秒为单位

    public class Countdown3 { private int lin; private int curSec; public Countdown3(int lin)throws Inte ...