socket socketserver

1.socket  模块

 

流程图

 

 

单线程,过程类似打电话

 

 

 

server端

#server端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Alex Lire
import socket
import subprocess #导入执行命令模块
ip_port=('127.0.0.1',9999) #定义元祖
#买手机
s=socket.socket()  #绑定协议,生成套接字
s.bind(ip_port)    #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式
s.listen(5)        #定义最大可以挂起胡链接数
#等待电话
while True:  #用来重复接收新的链接
    conn,addr=s.accept()   #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port
    #收消息
    while True: #用来基于一个链接重复收发消息
            try: #捕捉客户端异常关闭(ctrl+c)
                recv_data=conn.recv(1024) #收消息,阻塞
                if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出

                #发消息
                p=subprocess.Popen(str(recv_data,encoding='utf8'),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平
                                                                                                      # 台命令的标准输出是gbk编码,需要转换
                res=p.stdout.read()   #获取标准输出
                if len(res) == 0:   #执行错误命令,标准输出为空,
                    send_data='cmd err'
                else:
                    send_data=str(res,encoding='gbk')  #命令执行ok,字节gbk---->str---->字节utf-8

                send_data=bytes(send_data,encoding='utf8')


                #解决粘包问题
                ready_tag='Ready|%s' %len(send_data)
                conn.send(bytes(ready_tag,encoding='utf8')) #发送数据长度
                feedback=conn.recv(1024)  #接收确认信息
                feedback=str(feedback,encoding='utf8')

                if feedback.startswith('Start'):
                    conn.send(send_data)  #发送命令的执行结果
            except Exception:
                break
    #挂电话
    conn.close()

 

client端

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Alex Li
import socket
ip_port=('127.0.0.1',9999)
#买手机
s=socket.socket()
#拨号
s.connect(ip_port)  #链接服务端,如果服务已经存在一个好的连接,那么挂起

while True:        #基于connect建立的连接来循环发送消息
    send_data=input(">>: ").strip()
    if send_data == 'exit':break
    if len(send_data) == 0:continue
    s.send(bytes(send_data,encoding='utf8'))

    #解决粘包问题
    ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998
    ready_tag=str(ready_tag,encoding='utf8')
    if ready_tag.startswith('Ready'):#Ready|9998
        msg_size=int(ready_tag.split('|')[-1])  #获取待接收数据长度
    start_tag='Start'
    s.send(bytes(start_tag,encoding='utf8')) #发送确认信息

    #基于已经收到的待接收数据长度,循环接收数据
    recv_size=0
    recv_msg=b''
    while recv_size < msg_size:
        recv_data=s.recv(1024)
        recv_msg+=recv_data
        recv_size+=len(recv_data)
        print('MSG SIZE %s RECE SIZE %s' %(msg_size,recv_size))

    print(str(recv_msg,encoding='utf8'))
    #挂电话
s.close()

 

 

2socketserver 实现支持多客户端

上述ssh模拟客户端只能支持一定数量的客户端,受s.listen(0)参数限制。下面可以实现支持多客户端操作

SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求

 

 

 

  • ThreadingTCPServer

    ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互

  • 实现步骤:

    • 1.创建一个类,并继承SocketServer.BaseRequestHandler 的类
    • 2.在新类中需要创建一个handle的方法
    • 3.启动ThreadingTCPServer

代码如下:

 

import socketserver
import subprocess

class MyServer(socketserver.BaseRequestHandler):  #继承
    def handle(self):   #handle方法。注意此时send和recv时调用的self.request方法
        self.request.sendall(bytes('Welcome',encoding='utf-8'))
        while True:
            try:
                recv_data = self.request.recv(1024)
                if not recv_data: break
                p = subprocess.Popen(str(recv_data, encoding='utf-8'), shell=True, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                res = p.stdout.read()
                if not res:
                    send_data = p.stderr.read()
                else:
                    send_data = res
                if not send_data:
                    send_data = 'no output'.encode()

                data_size = len(send_data)
                self.request.send(bytes(str(data_size), encoding='utf-8'))
                self.request.recv(1024)
                self.request.send(send_data)
            except Exception:
                break



if __name__ == '__main__':

    server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer)    #启动server
    server.serve_forever()

 

PS:SocketServer.BaseRequestHandler类源码:其定义了三个方法:setup(),handle()he finish()
执行顺序为:setup(0-->handle()-->finish()

class BaseRequestHandler:

def __init__(self, request, client_address, server):
    self.request = request
    self.client_address = client_address
    self.server = server
    self.setup()
    try:
        self.handle()
    finally:
        self.finish()

def setup(self):
    pass

def handle(self):
    pass

def finish(self):
    pass
SocketServer.BaseRequestHandler

 

内部调用流程

  • 启动服务端程序
  • 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
  • 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
  • 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
  • 当客户端连接到达服务器
  • 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
  • 执行 ThreadingMixIn.process_request_thread 方法
  • 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
posted @ 2016-07-09 09:52  不是云  阅读(1228)  评论(0编辑  收藏  举报