socket实现并发

之前版本的服务端,在while true的大循环中,只要来一个连接直接就进入小循环当中了。

在小链接中,只要不跳出来,服务端将一直处于这个小的通信循环当中。不能并发。

soketserver版本:

1、类Mysever就是用来实例化函数handle方法的。

2、handle方法就是收发消息的过程(通信循环)。添加异常处理

3、连接循环在if __name__ == '__main__'当中写入,只有右键运行整个程序的时候才能执行

4、类TreadingTCPServer利用类MySever实例化得到一个对象s:将通信循环Mysever嵌套在 TreadingTCPServer 当中。

  实现多线程的服务端:实现并发,每来一个链接就可以通过mysever实例化建立一个收发消息handle函数来执行。

 

soketserver模块介绍:

虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也是Python标准库中很多服务器框架的基础。

socketserver模块可以简化网络服务器的编写,Python把网络服务抽象成两个主要的类,一个是Server类,用于处理连接相关的网络操作,另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。

socketsever中分为两大类:

第一类:sever类:专门处理链接。最基本的有五个:

它包含了种五种server类,BaseServer(不直接对外服务)。TCPServer使用TCP协议,UDPServer使用UDP协议,还有两个不常使用的,即UnixStreamServer和UnixDatagramServer,这两个类仅仅在unix环境下有用(AF_unix)。

BaseSever ------> TCPServer(UnixStreamServer)-------> UDPServer(UnixDatagramServer),

其中,TCPServer继承BaseSever,UDPServer继承TCPServer。

UnixStreamServer(只能处理Unix平台上的TCP)继承TCPServer,UnixDatagramServer继承UDPServer。这俩一般也不会用到。

#利用反射hasattr判断对象socket中是否含有AF_UNIX属性的地址家族

if hasattr(socket,"AF_UNIX")
  __all__.extend(["UnixSteamServer","UnixDatagramServer","ThreadingUnixStreamServer","ThreadingUnixDatagramServer"]) #若有,则在原来的基础上添加有关unix的一系列类。
#AF_Inet基于网络的地址家族

 跟并发有关的:

进程:ForkingTCPServer继承ForkingMixIn和TCPServer,ForkingUDPServer继承ForkingMixIn和UDPServer

线程:ThreadingTCPServer继承ThreadingMixIn和TCPServer,ThreadingUDPServer继承ThreadingMixIn和UDPServer

第二类:request类:处理通信

BaseRequestHandler()

StreamRequestHandler()

DatagramRequestHandler()

#服务端socketserver的TCP版本
import socketserver

'''
    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()

'''

class MyServer(socketserver.BaseRequestHandler):

    def handle(self):           #必须写,因为一定会需要调用。handle用于控制通讯循环
        print('conn is: ',self.request)   #conn
        print('addr is: ',self.client_address) #addr

        while True:
            try:
            #收消息
                data=self.request.recv(1024)
                if not data:break
                print('收到客户端的消息是',data,self.client_address)

                #发消息
                self.request.sendall(data.upper())

            except Exception as e:
                print(e)
                break

if __name__ == '__main__':
    s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) #多线程
    # ThreadingTCPServer中没有init,只能找他父类中的,他继承了ThreadingMixIn和TCPServer
    #在ThreadingMixIn中没有,找TCPServer中也没有,继续在TCPServer的父类BaseSever中找到__init__
'''
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    request_queue_size = 5
    allow_reuse_address = False    #不允许重新使用地址
    
    def __init__(self,server_address,RequestHandlerClass,bind_and_activate=True):
        BaseServer.__init__(self,server_address,RequestHandlerClass)
        self.socket = socket.socket(self.address_family,self.socket_type)    #就是上几行中init定义之前写好的
        
        if bind_and_activate:      #绑定IP并激活
            try:
                self.server_bind()   
                self.server_active()    #active中self.socket.listen(self.request_queue_size)
            except:
                self.server_close()
                raise
'''
    # s=socketserver.ForkingTCPServer(('127.0.0.1',8080),MyServer) #多进程
#以下两行是init中写好的,init产生一个套接字对象,经历一个实例化过程
    # self.server_address = server_address
    # self.RequestHandlerClass = RequestHandlerClass
    print(s.server_address)           #('127.0.0.1',8080)
    print(s.RequestHandlerClass)    #<class '__main__.MyServer'>
    print(MyServer)                    #<class '__main__.MyServer'>
    print(s.socket) #<socket.socket fd=236, family=AddressFamily.AF_INET,type=SocketKind.SOCK_STREAM,proto=0>
    print(s.server_address)
    
    s.serve_forever()    #最终在TCPServer的父类BaseServer中找到,
    # forever中会while死循环一直执行来接收链接,一个request就一个链接conn,client_address接收的就是客户端地址
#当中会执行self.process_request(request,client_address)   #在ThreadingMixIn中找到
#完成链接循环,MyServer(request,client_address,self)   这才是最终得到的
#同样Mysever中没有init,去找父类中的

 

##服务端socketserver的UDP版本,UDP当中用的不多
import socketserver

class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print(self.request)
        print('收到客户端的消息是',self.request[0])
        self.request[1].sendto(self.request[0].upper(),self.client_address)


if __name__ == '__main__':
    s=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer) #多线程
    #在父类中UDPServer的父类TCPServer中的init,此时会先经历UDPServer中的一系列参数赋值
'''
class UDPServer(TCPServer):
    allow_reuse_address = False
    socket_type = socket.SOKET_DGRAM
    max_packet_size = 8192
    
def __init__()    #产生UDP套接字,并进行绑定和激活
# UDPServer自己定义的激活函数server_activate什么都不用做  
'''
    s.serve_forever()

 认证客户端的链接合法性

如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现

#服务端
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'   #用于’加盐‘
def conn_auth(conn):
    '''
    认证客户端链接
    :param conn:
    :return:
    '''
    print('开始验证新链接的合法性')
    msg=os.urandom(32)   #产生32个字节的随机数
    conn.sendall(msg)    #发送给客户端
    h=hmac.new(secret_key,msg)   #hash验证的模块,对产生的msg数据加盐
    digest=h.digest()        #得到数值
    respone=conn.recv(len(digest))   #接收客户端返回的hash值
    return hmac.compare_digest(respone,digest)   #判断服务端和客户端的hash是否相同

def data_handler(conn,bufsize=1024):            #专门处理通信
    if not conn_auth(conn):
        print('该链接不合法,关闭')
        conn.close()
        return
    print('链接合法,开始通信')
    while True:
        data=conn.recv(bufsize)
        if not data:break
        conn.sendall(data.upper())

def server_handler(ip_port,bufsize,backlog=5):   
    '''
    只处理链接
    :param ip_port:
    :return:
    '''
    tcp_socket_server=socket(AF_INET,SOCK_STREAM)
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(backlog)
    while True:
        conn,addr=tcp_socket_server.accept()
        print('新连接[%s:%s]' %(addr[0],addr[1]))
        data_handler(conn,bufsize)

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    server_handler(ip_port,bufsize)

 

#客户端(合法)
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
    '''
    验证客户端到服务器的链接
    :param conn:
    :return:
    '''
    msg=conn.recv(32)
    h=hmac.new(secret_key,msg)
    digest=h.digest()
    conn.sendall(digest)

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    conn_auth(tcp_socket_client)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

 

#不知道加密方式
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

 

#知道认证方式,不知道加的什么盐
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os

secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
    '''
    验证客户端到服务器的链接
    :param conn:
    :return:
    '''
    msg=conn.recv(32)
    h=hmac.new(secret_key,msg)
    digest=h.digest()
    conn.sendall(digest)

def client_handler(ip_port,bufsize=1024):
    tcp_socket_client=socket(AF_INET,SOCK_STREAM)
    tcp_socket_client.connect(ip_port)

    conn_auth(tcp_socket_client)

    while True:
        data=input('>>: ').strip()
        if not data:continue
        if data == 'quit':break

        tcp_socket_client.sendall(data.encode('utf-8'))
        respone=tcp_socket_client.recv(bufsize)
        print(respone.decode('utf-8'))
    tcp_socket_client.close()

if __name__ == '__main__':
    ip_port=('127.0.0.1',9999)
    bufsize=1024
    client_handler(ip_port,bufsize)

 

posted on 2018-05-07 11:09  Josie_chen  阅读(346)  评论(0编辑  收藏  举报

导航