• python socket和socketserver


    Python提供了两个基本的socket模块。一个是socket,它提供了标准的BSD Socket API;另一个是socketServer,它提供了服务器中心类,可以简化网络服务器的开发。

            下面先简要介绍socket模块包含的类及其使用。

            1.开始了解socket模块前,先熟悉下Python的网络编程模块主要支持的两种Intent协议:TCP和UDP。TCP协议是一种面向连接的可靠协议,用于建立机器之间的双向通信流。UDP协议是一种较低级别的、以数据包为基础的协议(无连接传输模式)。与TCP不同,UDP信息不可靠。

             他们的区别如下图所示:左图为TCP连接协议,右图为UDP连接协议

                         

      2.socket模块的部分类方法介绍

    类方法

    说明

    socket.socket(family, type[,proto])

    创建并返回一个新的 socket对象

    socket.getfqdn(name)

    将使用点号分隔的 IP地址字符串转换成一个完整的域名

    socket.gethostbyname(hostname)

    将主机名解析为一个使用点号分隔的 IP地址字符串

    socket.gethostbyname_ex(name)

    它返回一个包含三个元素的元组,从左到右分别是给定地址的主要的主机名、同一IP地址的可选的主机名的一个列表、关于同一主机的同一接口的其它IP地址的一个列表(列表可能都是空的)。

    socket.gethostbyaddr(address)

    作用与gethostbyname_ex相同,只是你提供给它的参数是一个IP地址字符串

    Socket.getservbyname(service,protocol)

    它要求一个服务名(如'telnet'或'ftp')和一个协议(如'tcp'或'udp'),返回服务所使用的端口号

    socket.fromfd(fd, family, type)

    从现有的文件描述符创建一个 socket对象

     3.socket对象的部分方法介绍

    实例方法

    说明

    sock.bind( (adrs, port) )

    将 socket绑定到一个地址和端口上

    sock.accept()

    返回一个客户机 socket(带有客户机端的地址信息)

    sock.listen(backlog)

    将 socket设置成监听模式,能够监听 backlog 外来的连接请求

    sock.connect( (adrs, port) )

    将 socket连接到定义的主机和端口上

    sock.recv( buflen[, flags] )

    从 socket中接收数据,最多 buflen 个字符

    sock.recvfrom( buflen[, flags] )

    从 socket中接收数据,最多 buflen 个字符,同时返回数据来源的远程主机和端口号

    sock.send( data[, flags] )

    通过 socket发送数据

    sock.sendto( data[, flags], addr )

    通过 socket发送数据

    sock.close()

    关闭 socket

    sock.getsockopt( lvl, optname )

    获得指定 socket 选项的值

    sock.setsockopt( lvl, optname, val )

    设置指定 socket选项的值

    4.编写socket测试程序

    (a)编写server的步骤

    第一步是创建socket对象。调用socket构造函数。如:

    socket = socket.socket( family, type )

    family参数代表地址家族,可为AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用于同一台机器上的进程间通信。

    type参数代表套接字类型,可为SOCK_STREAM(流套接字)和SOCK_DGRAM(数据报套接字)。

    第二步是将socket绑定到指定地址。这是通过socket对象的bind方法来实现的:

    socket.bind( address ) 

    由AF_INET所创建的套接字,address地址必须是一个双元素元组,格式是(host,port)。host代表主机,port代表端口号。如果端口号正在使用、主机名不正确或端口已被保留,bind方法将引发socket.error异常。

    第三步是使用socket套接字的listen方法接收连接请求。

    socket.listen( backlog )

    backlog指定最多允许多少个客户连接到服务器。它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求。

    第四步是服务器套接字通过socket的accept方法等待客户请求一个连接。

    connection, address = socket.accept()

    调 用accept方法时,socket会时入“waiting”状态。客户请求连接时,方法建立连接并返回服务器。accept方法返回一个含有两个元素的元组(connection,address)。第一个元素connection是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。

    第 五步是处理阶段,服务器和客户端通过send和recv方法通信(传输 数据)。服务器调用send,并采用字符串形式向客户发送信息。send方法返回已发送的字符个数。服务器使用recv方法从客户接收信息。调用recv 时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv方法在接收数据时会进入“blocked”状态,最后返回一个字符 串,用它表示收到的数据。如果发送的数据量超过了recv所允许的,数据会被截短。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区 删除(以及自上次调用recv以来,客户可能发送的其它任何数据)。 

    传输结束,服务器调用socket的close方法关闭连接。

    (b)编写client的步骤

    首先创建一个socket以连接服务器:socket =socket.socket( family, type ) 

    使用socket的connect方法连接服务器。对于AF_INET家族,连接格式如下:

    socket.connect( (host,port) )

    host代表服务器主机名或IP,port代表服务器进程所绑定的端口号。如连接成功,客户就可通过套接字与服务器通信,如果连接失败,会引发socket.error异常。

    处理阶段,客户和服务器将通过send方法和recv方法通信。 

    Server:

    import socket
    port=8081
    s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    #从指定的端口,从任何发送者,接收UDP数据
    s.bind(('',port))
    print('正在等待接入...')
    while True:
        #接收一个数据
        data,addr=s.recvfrom(1024)
        print('Received:',data,'from',addr)

    Client:

    import socket
    port=8081
    host='localhost'
    s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    s.sendto(b'hello,this is a test info !',(host,port))

    很简单 。下面是TCP方式:

    Server:

    #-*- coding: utf-8 -*-
    from socket import *
    from time import ctime
    
    HOST=''
    PORT=12345
    BUFSIZ=1024
    ADDR=(HOST, PORT)
    sock=socket(AF_INET, SOCK_STREAM)
    
    sock.bind(ADDR)
    
    sock.listen(5)
    while True:
        print('waiting for connection')
        tcpClientSock, addr=sock.accept()
        print('connect from ', addr)
        while True:
            try:
                data=tcpClientSock.recv(BUFSIZ)
            except:
                print(e)
                tcpClientSock.close()
                break
            if not data:
                break
            s='Hi,you send me :[%s] %s' %(ctime(), data.decode('utf8'))
            tcpClientSock.send(s.encode('utf8'))
            print([ctime()], ':', data.decode('utf8'))
    tcpClientSock.close()
    sock.close()

    Client:

    #-*- coding: utf-8 -*-
    from socket import *
    
    class TcpClient:
        HOST='127.0.0.1'
        PORT=12345
        BUFSIZ=1024
        ADDR=(HOST, PORT)
        def __init__(self):
            self.client=socket(AF_INET, SOCK_STREAM)
            self.client.connect(self.ADDR)
    
            while True:
                data=input('>')
                if not data:
                    break
                self.client.send(data.encode('utf8'))
                data=self.client.recv(self.BUFSIZ)
                if not data:
                    break
                print(data.decode('utf8'))
                
    if __name__ == '__main__':
        client=TcpClient()

    上面的TCP方式有个问题,不能退出,好吧,我们改造一下,使这个程序可以发送quit命令以退出:

    Server:

    #-*- coding: utf-8 -*-
    from socket import *
    from time import ctime
    from time import localtime
    import time
    
    HOST=''
    PORT=1122  #设置侦听端口
    BUFSIZ=1024
    ADDR=(HOST, PORT)
    sock=socket(AF_INET, SOCK_STREAM)
    
    sock.bind(ADDR)
    
    sock.listen(5)
    #设置退出条件
    STOP_CHAT=False
    while not STOP_CHAT:
        print('等待接入,侦听端口:%d' % (PORT))
        tcpClientSock, addr=sock.accept()
        print('接受连接,客户端地址:',addr)
        while True:
            try:
                data=tcpClientSock.recv(BUFSIZ)
            except:
                #print(e)
                tcpClientSock.close()
                break
            if not data:
                break
            #python3使用bytes,所以要进行编码
            #s='%s发送给我的信息是:[%s] %s' %(addr[0],ctime(), data.decode('utf8'))
            #对日期进行一下格式化
            ISOTIMEFORMAT='%Y-%m-%d %X'
            stime=time.strftime(ISOTIMEFORMAT, localtime())
            s='%s发送给我的信息是:%s' %(addr[0],data.decode('utf8'))
            tcpClientSock.send(s.encode('utf8'))
            print([stime], ':', data.decode('utf8'))
            #如果输入quit(忽略大小写),则程序退出
            STOP_CHAT=(data.decode('utf8').upper()=="QUIT")
            if STOP_CHAT:
                break
    tcpClientSock.close()
    sock.close()

    Client:

    #-*- coding: utf-8 -*-
    from socket import *
    
    class TcpClient:
        #测试,连接本机
        HOST='127.0.0.1'
        #设置侦听端口
        PORT=1122 
        BUFSIZ=1024
        ADDR=(HOST, PORT)
        def __init__(self):
            self.client=socket(AF_INET, SOCK_STREAM)
            self.client.connect(self.ADDR)
    
            while True:
                data=input('>')
                if not data:
                    break
                #python3传递的是bytes,所以要编码
                self.client.send(data.encode('utf8'))
                print('发送信息到%s:%s' %(self.HOST,data))
                if data.upper()=="QUIT":
                    break            
                data=self.client.recv(self.BUFSIZ)
                if not data:
                    break
                print('从%s收到信息:%s' %(self.HOST,data.decode('utf8')))
                
                
    if __name__ == '__main__':
        client=TcpClient()


    下面先简要介绍socketserver模块包含的类及其使用

    SocketServer是标准库中一个高级别的模块。用于简化网络客户与服务器的实现。模块中,已经实现了一些可供使用的类。

    我们将再次实现之前的那个基本TCP的例子。你会注意到新实现与之前有很多相似之处,但你也要注意到,现在很多繁杂的事情已经被封装好了,你不用再去关心那个样板代码了。例子给出的是一个最简单的同步服务器。

    为了要隐藏实现的细节。我们现在写程序时会使用类,这是与之前代码的另一个不同。用面向对象的方法可以帮助我们更好的组织数据与逻辑功能。你也会注意到,我们的程序现在是“事件驱动”了。这就意味着,只有在事件出现的时候,程序才有“反应”。

    在之前的服务循环中,我们阻塞等待请求,有请求来的时候就处理请求,然后再回去继续等待。现在的服务循环中,就不用在服务器里写代码了,改成定义一个处理器,服务器在收到进来的请求的时候,可以调用你的处理函数。

    1. 创建一个SocketServerTCP服务器:

    在代码中,先导入我们的服务器类,然后像之前一样定义主机常量。主机常量后就是我们的请求处理器类,然后是启动代码。在下面的代码片断中可以看到更多细节。

    #!/usr/bin/env python
     
    from SocketServer import (TCPServer as TCP,
                              StreamRequestHandler as SRH)
    from time import ctime
     
    HOST = ''
    PORT = 12346
    ADDR = (HOST, PORT)
     
    class MyRequestHandler(SRH):
        def handle(self):
            print '...connected from:', self.client_address
            self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))
     
    tcpServ = TCP(ADDR, MyRequestHandler)
    print 'waiting for connection...'
    tcpServ.serve_forever()

    我们从SocketServer的StreamRequestHandler类中派生出一个子类,并重写handle()函数。在BaseRequest类中,这个函数什么也不做。在有客户消息进来的时候,handle()函数就会被调用。StreamRequestHandler 类支持像操作文件对象那样操作输入输出套接字。我们可以用readline()函数得到客户消息,用write()函数把字符串发给客户。

    为了保持一致性,我们要在客户与服务器两端的代码里都加上回车与换行。实际上,你在代码中看不到这个,因为,我们重用了客户传过来的回车与换行。

    2. 创建SocketServerTCP客户端

    #!/usr/bin/env python
     
    from socket import *
     
    HOST = 'localhost'
    PORT = 12346
    BUFSIZE = 1024
    ADDR = (HOST, PORT)
     
    while True:
        tcpCliSock = socket(AF_INET, SOCK_STREAM)
        tcpCliSock.connect(ADDR)
        data = raw_input('>')
        if not data:
            break
        tcpCliSock.send('%s
    ' % data)
        data = tcpCliSock.recv(BUFSIZE)
        if not data:
            break
        print data.strip()
        tcpCliSock.close()

    SocketServer的请求处理器的默认行为是接受连接,得到请求,然后就关闭连接。这使得我们不能在程序的运行时,一直保持连接状态,要每次发送数据到服务器的时候都要创建一个新的套接字。这种行为使得TCP 服务器的行为有些像UDP服务器。不过,这种行为也可以通过重写请求处理器中相应的函数来改变。

    现在,我们的客户端有点完全不一样了(我们得每次都创建一个连接)。其它的小区别在服务器代码的逐行解释中已经看到了:我们使用的处理器类像文件一样操作套接字,所以我们每次都要发送行结束字符(回车与换行)。服务器只是保留并重用我们发送的行结束字符。当我们从服务器得到数据的时候,我们使用strip()函数去掉它们,然后使用print语句提供的回车。

     

        3.   使用SocketServer处理多连接

        上面的例子一次只能连接一个客户机并出力它的请求,如果要处理多连接问题,那么有三种主要的方法能实现这个目的:分叉(forking)、线程(threading)以及异步I/O(asynchronous I/O)。通过对SocketServer服务器使用混入类(mix-in class),派生进程和线程很容易处理。即使要自己实现它们,这些方法也很容易使用。它们确实有缺点:分叉占据资源,并且如果有太多的客户端时分叉不能很好分叉(尽管如此,对于合理数量的客户端,分叉在现代的UNIX或者Linux系统中是很高效的,如果有一个多CPU系统,那系统效率会更高);线程处理能导致同步问题。使用SocketServer框架创建分叉或者线程服务器非常简单: 

    分叉SocketServer服务器:

    #!/usr/bin/env python
     
    from SocketServer import (TCPServer as TCP,
                              StreamRequestHandler as SRH,
                              ForkingMixIn as FMI)
    from time import ctime
     
    HOST = ''
    PORT = 12346
    ADDR = (HOST, PORT)
     
    class Server(FMI, TCP):
        pass
     
    class MyRequestHandler(SRH):
        def handle(self):
            print '...connected from:', self.client_address
            self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))
     
    tcpServ = Server(ADDR, MyRequestHandler)
    print 'waiting for connection...'
    tcpServ.serve_forever()

    多线程SocketServer服务器:

    #!/usr/bin/env python
     
    from SocketServer import (TCPServer as TCP,
                              StreamRequestHandler as SRH,
                              ThreadingMixIn as TMI)
    from time import ctime
     
    HOST = ''
    PORT = 12346
    ADDR = (HOST, PORT)
     
    class Server(TMI, TCP):
        pass
     
    class MyRequestHandler(SRH):
        def handle(self):
            print '...connected from:', self.client_address
            self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))
     
    tcpServ = Server(ADDR, MyRequestHandler)
    print 'waiting for connection...'
    tcpServ.serve_forever()






  • 相关阅读:
    mvc是如何工作的
    MVC4 AJAX Unobtrusive
    MVC4 jQuery UI自动完成
    MVC4Html Helper
    MVC4 Layouts布局视图局部视图
    理解mvc
    ASP.NET MVC HTMLHELPER类的方法总结
    I2C中的重复起始条件到底是什么意思
    release, retain, autorelease 与 AT, MT, AMT
    CMSIS SVD(System View Description)小解
  • 原文地址:https://www.cnblogs.com/panwenbin-logs/p/5655130.html
Copyright © 2020-2023  润新知