zoukankan      html  css  js  c++  java
  • Python自动化开发学习的第八周----socket网络编程

    socket网络编程

    1.什么是socket

    socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

    基本上,Socket 是任何一种计算机网络通讯中最基础的内容。例如当你在浏览器地址栏中输入 http://www.cnblogs.com/ 时,你会打开一个套接字,然后连接到 http://www.cnblogs.com/ 并读取响应的页面然后然后显示出来。而其他一些聊天客户端如 gtalk 和 skype 也是类似。任何网络通讯都是通过 Socket 来完成的。

    Python 官方关于 Socket 的函数请看 http://docs.python.org/library/socket.html

    socket和file的区别:

      1、file模块是针对某个指定文件进行【打开】【读写】【关闭】

      2、socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

     

    2.我们来创建一个socket的服务器

     1 import socket
     2 
     3 server = socket.socket()
     4 server.bind(("localhost",9999))
     5 server.listen(5)
     6 
     7 conn,addr = server.accept()
     8 
     9 res= str(conn.recv(1024),encoding="utf-8")
    10 print(res)
    11 conn.send(bytes("I am is server!!",encoding="utf-8"))
    socket server
    1 import socket
    2 
    3 client = socket.socket()
    4 client.connect(("localhost",9999))
    5 
    6 client.send(bytes("I am is client!!",encoding="utf-8"))
    7 res = str(client.recv(1024),encoding="utf-8")
    8 print(res)
    socket client

    3.socket的其他功能

    sk.bind(address)

      s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

    sk.listen(backlog)

      开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

          backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
          这个值不能无限大,因为要在内核中维护连接队列

    sk.setblocking(bool)

      是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

    sk.accept()

      接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。

      接收TCP 客户的连接(阻塞式)等待连接的到来

    sk.connect(address)

      连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

    sk.connect_ex(address)

      同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

    sk.close()

      关闭套接字

    sk.recv(bufsize[,flag])

      接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

    sk.recvfrom(bufsize[.flag])

      与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

    sk.send(string[,flag])

      将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

    sk.sendall(string[,flag])

      将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

          内部通过递归调用send,将所有内容发送出去。

    sk.sendto(string[,flag],address)

      将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

    sk.settimeout(timeout)

      设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

    sk.getpeername()

      返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

    sk.getsockname()

      返回套接字自己的地址。通常是一个元组(ipaddr,port)

    sk.fileno()

      套接字的文件描述符

    4.socket实例

    智能机器人:

     1 import socket
     2 
     3 server = socket.socket()
     4 server.bind(("localhost",9999))
     5 server.listen(5)
     6 
     7 while True:
     8     conn,addr = server.accept()
     9     conn.sendall("欢迎致电 10086,请输入1xxx,0转人工服务")
    10     while True:
    11         data = conn.recv(1024)
    12         if data == "exit":
    13             exit()
    14         elif data == "0":
    15             conn.send("你可能被录音")
    16         else:
    17             conn.send("请重新输入")
    18 
    19 conn.close()
    server
     1 import socket
     2 
     3 client = socket.socket()
     4 client.connect(("localhost",9999))
     5 
     6 while True:
     7     data = client.recv(1024)
     8     print ('receive:',data)
     9     inp = input('please input:')
    10     client.sendall(inp)
    11     if inp == 'exit':
    12         break
    13 
    14 client.close()
    client

    多次的数据交互怎么实现的?

     1 import socket
     2 
     3 server = socket.socket() #获得socket实例
     4 
     5 server.bind(("localhost",9998)) #绑定ip port
     6 server.listen()  #开始监听
     7 print("等待客户端的连接...")
     8 conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
     9 print("新连接:",addr )
    10 while True:
    11 
    12     data = conn.recv(1024)
    13 
    14     print("收到消息:",data)
    15     conn.send(data.upper())
    16 
    17 server.close()
    server
     1 import socket
     2 
     3 client = socket.socket()
     4 
     5 client.connect(("localhost",9998))
     6 
     7 while True:
     8     msg = input(">>:").strip()
     9     if len(msg) == 0:continue
    10     client.send( msg.encode("utf-8") )
    11 
    12     data = client.recv(1024)
    13     print("来自服务器:",data)
    14 
    15 client.close()
    client

    这样就实现了多次的交互,但是有个问题是一旦客户端断开,服务器就进入死循环,因为客户端断开后,服务端收不到数据,但是不会报错,所以就进入了死循环~~

    解决办法就是服务端加上一个判断服务器端收到的数据是否为空,若为空断开

    import socket
    
    server = socket.socket() #获得socket实例
    
    server.bind(("localhost",9998)) #绑定ip port
    server.listen()  #开始监听
    print("等待客户端的连接...")
    conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    print("新连接:",addr )
    while True:
        data = conn.recv(1024)
        
        if not data:
            print("客户端已断开。。。")
            break
    
        print("收到消息:",data)
        conn.send(data.upper())
    
    server.close()

    5.socket实现多连接处理

    上面的代码可以看出来实现了服务器和客户端的之间的交互,但是有一个问题是,客户端一旦断开,服务器端也会断开。因为服务器端就一个while循环,客户端一断,服务器收不到数据,就会直接break跳出循环,然后退出程序。

    我们要实现的功能是一个服务器端要对多个客户端的,不能一个连接完断开后,服务器就不能用了,我们想要的是服务器端不能断,在接完一个客户端后,还能在接下一个客户端。

    conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    

    上面这句话负责等待并接收新连接,对于上面那个程序,其实在while break之后,只要让程序再次回到上面这句代码这,就可以让服务端继续接下一个客户啦。 

    import socket
    
    server = socket.socket() #获得socket实例
    
    server.bind(("localhost",9998)) #绑定ip port
    server.listen()  #开始监听
    
    while True:#当一个客户端断开后会进入下一个循环
        print("等待客户端的连接...")
        conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
        print("新连接:",addr )
        while True:
            data = conn.recv(1024)
            if not data:
                print("客户端已断开。。。")
                break
            print("收到消息:",data)
            conn.send(data.upper())
    
    server.close()
    

    6.socket实现简单的ssh功能

    socket连接之后实现了交互数据,那么是不是可以实现ssh的简单功能呢?输入命令返回执行结果

     1 import socket,os
     2 
     3 server = socket.socket() #获得socket实例
     4 
     5 server.bind(("localhost",9998)) #绑定ip port
     6 server.listen()  #开始监听
     7 
     8 while True:#当一个客户端断开后会进入下一个循环
     9     print("等待客户端的连接...")
    10     conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    11     print("新连接:",addr )
    12     while True:
    13         data = conn.recv(1024)
    14         if not data:
    15             print("客户端已断开。。。")
    16             break
    17         print("收到消息:",data)
    18         res = os.popen(data.decode()).read()  #接受字符串,执行结果也是字符串
    19 
    20         conn.send(res.encode("utf-8"))
    21         print("send done")
    22 
    23 server.close()
    24 
    25 #socketserver 支持多次交互
    server_ssh
     1 import socket
     2 
     3 client = socket.socket()
     4 
     5 client.connect(("localhost",9998))
     6 
     7 while True:
     8     msg = input(">>:").strip()
     9     if len(msg) == 0:continue
    10     client.send( msg.encode("utf-8") )
    11     data = client.recv(1024)
    12     print("来自服务器:",data.decode())
    13    
    14 
    15 client.close()
    16 
    17 #socket客户端支持多交互
    client_ssh 

     但是上面的代码在接受超过1024的数据时就出现了

    >>:ipconfig
    来自服务器: 
    Windows IP 配置
    
    
    以太网适配器 本地连接 3:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       本地链接 IPv6 地址. . . . . . . . : fe80::949a:6c99:a549:be3%33
       IPv4 地址 . . . . . . . . . . . . : 192.168.12.136
       子网掩码  . . . . . . . . . . . . : 255.255.255.0
       默认网关. . . . . . . . . . . . . : 192.168.12.254
    
    以太网适配器 本地连接 2:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
    
    隧道适配器 isatap.{ECCF9E67-37F2-43F7-9C20-C31636FEF348}:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
    
    隧道适配器 isatap.{5B46B469-AA20-4D7F-AA17-A2C8FBC6B97D}:
    
       媒体状态  . . . . . . . . . . . . : 媒体已断开
       连接特定的 DNS 后缀 . . . . . . . : 
    
    隧道适配器 本地连接* 37:
    
       连接特定的 DNS 后缀 . . . . . . . : 
       IPv6 地址 . . . . . . . . . . . . : 2001:0:9d38:6ab8:2c1c:9bc:3f57:f377
       本地链接 IPv6 地址. . 
    >>:dir
    来自服务器: . . . . . . : fe80::2c1c:9bc:3f57:f377%60
       默认网关. . . . . . . . . . . . . : ::
    
    >>:pwd
    来自服务器:  驱动器 C 中的卷没有标签。
     卷的序列号是 7CE6-567F
    
     C:pythonday11 的目录
    
    2017-07-05  23:19    <DIR>          .
    2017-07-05  23:19    <DIR>          ..
    2017-07-05  23:19               336 c_web.py
    2017-07-05  23:18               667 s_client.py
    2017-07-05  23:08             1,007 s_server.py
    2017-07-05  23:19               855 s_web.py
                   4 个文件          2,865 字节
                   2 个目录 67,487,215,616 可用字节
    

    这个完全打乱了原来的设想,输入第二次命令时返回之前没有显示的数据,这就不对了。怎么办呢?

     1 import socket,os
     2 
     3 server = socket.socket() #获得socket实例
     4 
     5 server.bind(("localhost",9998)) #绑定ip port
     6 server.listen()  #开始监听
     7 
     8 while True:#当一个客户端断开后会进入下一个循环
     9     print("等待客户端的连接...")
    10     conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    11     print("新连接:",addr )
    12     while True:
    13         data = conn.recv(1024)
    14         if not data:
    15             print("客户端已断开。。。")
    16             break
    17         print("收到消息:",data)
    18         res = os.popen(data.decode()).read()  #接受字符串,执行结果也是字符串
    19 
    20         print("before send",len(res))
    21         if len(res) == 0:
    22             res = "cmd is nothing"
    23 
    24         conn.send(str(len(res.encode())).encode("utf-8"))  #先发送大小到客户端
    25         conn.send(res.encode("utf-8"))
    26         print("send done")
    27 
    28 server.close()
    29 
    30 #socketserver 支持多次交互
    server_ssh_new
     1 import socket
     2 
     3 client = socket.socket()
     4 
     5 client.connect(("localhost",9998))
     6 
     7 while True:
     8     msg = input(">>:").strip()
     9     if len(msg) == 0:continue
    10     client.send( msg.encode("utf-8") )
    11 
    12     cmd_res_size = client.recv(1024) #接受命令结果的长度
    13     print("接收到的大小",cmd_res_size)
    14 
    15     received_size = 0
    16 
    17     while received_size < int(cmd_res_size.decode()):
    18         data = client.recv(1024)
    19         received_size += len(data)
    20         print("来自服务器:",data.decode())
    21     else:
    22         print("res received done....",received_size)
    23         #print(data.decode())
    24 
    25 
    26 client.close()
    27 
    28 #socket客户端支持多交互
    client_ssh_new

     这样就解决了问题,这是在windows上测试的,但是在linux上测试还是出现了问题

    >>:df
    接收到的大小 b'395Filesystem     1K-blocks    Used Available Use% Mounted on
    /dev/sda1       20510288 1744208  17701172   9% /
    devtmpfs          932920       0    932920   0% /dev
    tmpfs             942000       0    942000   0% /dev/shm
    tmpfs             942000   16720    925280   2% /run
    tmpfs             942000       0    942000   0% /sys/fs/cgroup
    tmpfs             188400       0    188400   0% /run/user/0
    '
    Traceback (most recent call last):
      File "s_client.py", line 17, in <module>
        while received_size < int(cmd_res_size.decode()):
    ValueError: invalid literal for int() with base 10: '395Filesystem     1K-blocks    Used Available Use% Mounted on
    /dev/sda1       20510288 1744208  17701172   9% /
    devtmpfs          932920       0    932920   0% /dev
    tmpfs             942000      
    

    这个问题出现主要是服务器端的2个conn.send是紧挨着的,

    解决办法是在2个conn.send之间交互一次

     1 import socket,os
     2 
     3 server = socket.socket() #获得socket实例
     4 
     5 server.bind(("localhost",9998)) #绑定ip port
     6 server.listen()  #开始监听
     7 
     8 while True:#当一个客户端断开后会进入下一个循环
     9     print("等待客户端的连接...")
    10     conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    11     print("新连接:",addr )
    12     while True:
    13         data = conn.recv(1024)
    14         if not data:
    15             print("客户端已断开。。。")
    16             break
    17         print("收到消息:",data)
    18         res = os.popen(data.decode()).read()  #接受字符串,执行结果也是字符串
    19 
    20         print("before send",len(res))
    21         if len(res) == 0:
    22             res = "cmd is nothing"
    23 
    24         conn.send(str(len(res.encode())).encode("utf-8"))  #先发送大小到客户端
    25         ack = conn.recv(1024)
    26         conn.send(res.encode("utf-8"))
    27         print("send done")
    28 
    29 server.close()
    ssh_server_new2
     1 import socket
     2 
     3 client = socket.socket()
     4 
     5 client.connect(("localhost",9998))
     6 
     7 while True:
     8     msg = input(">>:").strip()
     9     if len(msg) == 0:continue
    10     client.send( msg.encode("utf-8") )
    11 
    12     cmd_res_size = client.recv(1024) #接受命令结果的长度
    13     print("接收到的大小",cmd_res_size)
    14 
    15     client.send("我要交互一次".encode("utf-8"))
    16 
    17     received_size = 0
    18 
    19     while received_size < int(cmd_res_size.decode()):
    20         data = client.recv(1024)
    21         received_size += len(data)
    22         print("来自服务器:",data.decode())
    23     else:
    24         print("res received done....",received_size)
    25         #print(data.decode())
    26 
    27 
    28 client.close()
    ssh_client_new2

    7.socket实现文件的传送

     命令数据可以交互传送,那么文件也是可以的,文件传送也就是数据的传送。有以下步骤;

    1.读取文件名

    2.检测文件是否存在

    3.打开文件

    4.检测文件的大小

    5.发送文件大小给客户端

    6.等客户端确认

    7.开始边读数据边发数据

    8.md5 sum

     1 import socket,os
     2 
     3 server = socket.socket() #获得socket实例
     4 server.bind(("localhost",9997)) #绑定ip port
     5 server.listen()  #开始监听
     6 
     7 while True:#当一个客户端断开后会进入下一个循环
     8     print("等待客户端的连接...")
     9     conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    10     print("新连接:",addr )
    11     while True:
    12         data = conn.recv(1024)
    13         if not data:
    14             print("客户端已断开。。。")
    15             break
    16 
    17         cmd, filename = data.decode().split()
    18         print(filename)
    19         if os.path.isfile(filename):
    20             f = open(filename,"rb")
    21             file_size = os.stat(filename).st_size
    22             conn.send( str(file_size).encode() ) #发送文件的大小
    23             conn.recv(1024)
    24             for line in f:
    25                 conn.send(line)
    26             f.close()
    27             
    28         print("send done")
    29 
    30 server.close()
    ftp_socket_server
     1 import socket
     2 
     3 client = socket.socket()
     4 client.connect(("localhost",9997))
     5 
     6 while True:
     7     msg = input(">>:").strip()
     8     if len(msg) == 0:continue
     9 
    10     if msg.startswith("get"):
    11         client.send(msg.encode())
    12         server_response = client.recv(1024)   #接受文件的大小
    13         print("server_response:",server_response)
    14         client.send(b"ready to recv file")
    15         file_total_size = int(server_response.decode())
    16         received_size = 0
    17         filename = msg.split()[1]
    18         f = open(filename + ".new","wb")
    19         while received_size <file_total_size:
    20             data = client.recv(1024)
    21             received_size += len(data)
    22             f.write(data)
    23         else:
    24             print("file recv done..",received_size,file_total_size)
    25             f.close()
    26 
    27 client.close()
    ftp_socket_client

    这样就可以传送文件了

    那么怎么添加MD5呢,可以添加hashlib模块,并修改代码

     1 import socket,os
     2 import hashlib
     3 
     4 server = socket.socket() #获得socket实例
     5 server.bind(("localhost",9997)) #绑定ip port
     6 server.listen()  #开始监听
     7 
     8 while True:#当一个客户端断开后会进入下一个循环
     9     print("等待客户端的连接...")
    10     conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    11     print("新连接:",addr )
    12     while True:
    13         data = conn.recv(1024)
    14         if not data:
    15             print("客户端已断开。。。")
    16             break
    17 
    18         cmd, filename = data.decode().split()
    19         print(filename)
    20         if os.path.isfile(filename):
    21             f = open(filename,"rb")
    22             m = hashlib.md5
    23             file_size = os.stat(filename).st_size
    24             conn.send( str(file_size).encode() ) #发送文件的大小
    25             conn.recv(1024)
    26             for line in f:
    27                 m.update(line)
    28                 conn.send(line)
    29             print("file md5:",m.hexdigest())
    30             f.close()
    31             conn.send(m.hexdigest().encode()) #发送md5值
    32 
    33         print("send done")
    34 
    35 server.close()
    ftp_socket_server_md5
     1 import socket
     2 import hashlib
     3 
     4 client = socket.socket()
     5 client.connect(("localhost",9997))
     6 
     7 while True:
     8     msg = input(">>:").strip()
     9     if len(msg) == 0:continue
    10 
    11     if msg.startswith("get"):
    12         client.send(msg.encode())
    13         server_response = client.recv(1024)   #接受文件的大小
    14         print("server_response:",server_response)
    15         client.send(b"ready to recv file")
    16         file_total_size = int(server_response.decode())
    17         received_size = 0
    18         filename = msg.split()[1]
    19         f = open(filename + ".new","wb")
    20         m = hashlib.md5()
    21         while received_size <file_total_size:
    22             data = client.recv(1024)
    23             received_size += len(data)
    24             m.update(data)
    25             f.write(data)
    26         else:
    27             new_file_md5 = m.hexdigest()
    28             print("file recv done..",received_size,file_total_size)
    29             f.close()
    30         server_file_md5 = client.recv(1024)
    31         print("server file md5:",server_file_md5)
    32         print("client file md5:",new_file_md5)
    33 
    34 client.close()
    ftp_socket_client_md5

     那么现在有一个问题就是在服务器端上

                for line in f:
                    m.update(line)
                    conn.send(line)
                print("file md5:",m.hexdigest())
                f.close()
                conn.send(m.hexdigest().encode()) #发送md5值
    

    2个conn.send之间紧挨着,可能会出现粘包的现象,解决办法是在传送大小的时候就确认出了要传送的文件的大小,我们只需要收取到相等的数据大小即可

     1 import socket,os
     2 import hashlib
     3 
     4 server = socket.socket() #获得socket实例
     5 server.bind(("localhost",9997)) #绑定ip port
     6 server.listen()  #开始监听
     7 
     8 while True:#当一个客户端断开后会进入下一个循环
     9     print("等待客户端的连接...")
    10     conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
    11     print("新连接:",addr )
    12     while True:
    13         data = conn.recv(1024)
    14         if not data:
    15             print("客户端已断开。。。")
    16             break
    17 
    18         cmd, filename = data.decode().split()
    19         print(filename)
    20         if os.path.isfile(filename):
    21             f = open(filename,"rb")
    22             m = hashlib.md5()
    23             file_size = os.stat(filename).st_size
    24             conn.send( str(file_size).encode() ) #发送文件的大小
    25             conn.recv(1024)
    26             for line in f:
    27                 m.update(line)
    28                 conn.send(line)
    29             print("file md5:",m.hexdigest())
    30             f.close()
    31             conn.send(m.hexdigest().encode()) #发送md5值
    32 
    33         print("send done")
    34 
    35 server.close()
    ftp_socket_server_new
     1 import socket
     2 import hashlib
     3 
     4 client = socket.socket()
     5 client.connect(("localhost",9997))
     6 
     7 while True:
     8     msg = input(">>:").strip()
     9     if len(msg) == 0:continue
    10 
    11     if msg.startswith("get"):
    12         client.send(msg.encode())
    13         server_response = client.recv(1024)   #接受文件的大小
    14         print("server_response:",server_response)
    15         client.send(b"ready to recv file")
    16         file_total_size = int(server_response.decode())
    17         received_size = 0
    18         filename = msg.split()[1]
    19         f = open(filename + ".new","wb")
    20         m = hashlib.md5()
    21 
    22         while received_size <file_total_size:
    23             if file_total_size - received_size >1024:  #需要收的不止一次
    24                 size = 1024
    25             else:#最后一次,剩多少收取多少
    26                 size = file_total_size - received_size
    27                 print("last size:",size)
    28 
    29             data = client.recv(size)
    30             received_size += len(data)
    31             m.update(data)
    32             f.write(data)
    33         else:
    34             new_file_md5 = m.hexdigest()
    35             print("file recv done..",received_size,file_total_size)
    36             f.close()
    37         server_file_md5 = client.recv(1024)
    38         print("server file md5:",server_file_md5)
    39         print("client file md5:",new_file_md5)
    40 
    41 client.close()
    ftp_socket_client_new

    8.socketserver

    我们学习了利用socket模块创建socket通信服务,但细心学习后就会发现利用socket模块创建的服务无法进行多进程的处理,当需要进行大量请求处理时,请求就会阻塞在队列中,甚至发生请求丢弃。并且如果我们需要大量的socket时,就需要重复创建许多socket、绑定端口..... ,对于程序员来说意味着重复书写大量无意义代码。

    那有没有一种方式既能简化书写流程又能实现多线程开发呢 ? 答案是肯定的,这就是SocketServer模块。

    SocketServer简化了网络服务器的编写。在进行socket创建时,使用SocketServer会大大减少创建的步骤,并且SocketServer使用了select它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。

    创建一个socketserver 至少分以下几步:

    First, you must create a request handler class by subclassing the BaseRequestHandlerclass and overriding its handle() method; this method will process incoming requests.
    首先,你必须创建一个请求处理类,并且这个类要继承BaseRequestHandler,并且还要重写父类的handle()
      
    Second, you must instantiate one of the server classes, passing it the server’s address and the request handler class.
    第二,你必须实例化一个类TCPServer,并且传递server ip和你上面创建的请求处理类给这个TCPServer

    Then call the handle_request() or serve_forever() method of the server object to process one or many requests.
    server.handle_request() 只处理一个请求
    server.serve_forever() 处理多个请求,并永远执行

    Finally, call server_close() to close the socket.
    关闭socket

     1 import socketserver
     2 
     3 class MyTCPHandler(socketserver.BaseRequestHandler):
     4     """
     5     The request handler class for our server.
     6 
     7     It is instantiated once per connection to the server, and must
     8     override the handle() method to implement communication to the
     9     client.
    10     """
    11 
    12     def handle(self):
    13         # self.request is the TCP socket connected to the client
    14         self.data = self.request.recv(1024).strip()
    15         print("{} wrote:".format(self.client_address[0]))
    16         print(self.data)
    17         # just send back the same data, but upper-cased
    18         self.request.sendall(self.data.upper())
    19 
    20 if __name__ == "__main__":
    21     HOST, PORT = "localhost", 9999
    22 
    23     # Create the server, binding to localhost on port 9999
    24     server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    25 
    26     # Activate the server; this will keep running until you
    27     # interrupt the program with Ctrl-C
    28     server.serve_forever()
    socket_server

     但你发现,上面的代码,依然不能同时处理多个连接,擦,那我搞这个干嘛?别急,不是不能处理多并发,如果你想,你还要启用多线程,多线程我们现在还没讲,但你大体知道,有了多线程,就能同时让cpu干多件事了就行先。

    让你的socketserver并发起来, 必须选择使用以下一个多并发的类

    class socketserver.ForkingTCPServer

    class socketserver.ForkingUDPServer

    class socketserver.ThreadingTCPServer

    class socketserver.ThreadingUDPServer

    so 只需要把下面这句 

    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    

    换成下面这个,就可以多并发了,这样,客户端每连进一个来,服务器端就会分配一个新的线程来处理这个客户端的请求

    server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    
  • 相关阅读:
    函数防抖与函数节流 封装好的debounce和throttle函数
    机顶盒
    getchar() putchar()
    【整】char、varchar、nchar、nvarchar的区别
    主机名
    主机
    java中的匿名内部类总结
    智能路由器又多一个玩家——乐视TV
    乐视开始折腾路由器,小米与极路由还会好过吗?
    带你认识什么是路由器
  • 原文地址:https://www.cnblogs.com/garrett0220/p/7117815.html
Copyright © 2011-2022 走看看