zoukankan      html  css  js  c++  java
  • python周报第九周

    0.本周知识点预览

    •  网络编程基础  
      • socket
      • socketserver

    1.网络编程基础

    1.socket

     利用socket,我们就可以写网络编程的服务端和客户端实现通信了,附上两端的基本实现方法图:

    从图上可以看到,网络编程需要服务端和客户端,应该先起服务端,再起客户端。

    服务端的大致步骤为:

    1. 服务端实例化一个socket对象
    2. 服务端绑定特定的IP和端口
    3. 服务端监听所绑定的地址和端口
    4. 服务端等待连接
    5. 当客户端连接时,收到客户端的数据,经过处理后发送回客户端
    6. 循环步骤5,一直到结束连接
    7. 服务端关闭连接

    客户端的大致步骤为:

    1. 客户端实例化一个socket对象
    2. 客户端连接服务端所监听的地址和端口
    3. 客户端建立连接
    4. 客户端发送数据,等待服务端处理后,接收数据
    5. 循环步骤4,一直到结束连接
    6. 客户端关闭连接

    这里附上关于socket最简单的代码:

    服务端:

    import socket                   ###导入socket模块
    
    s = socket.socket()                 ###实例化一个socket对象
    server_addr = ("127.0.0.1",12345)
    s.bind(server_addr)             ###绑定IP和端口
    s.listen(1)             ###在拒绝连接前,最大接收挂起的客户端为1个
    conn,addr = s.accept()              ###等待接收数据
    while True:
        try:
            recv_data = conn.recv(1024)             ###接收数据,最多收1024个字节,接收的类型必须是bytes类型
            send_data = str(recv_data,encoding="utf-8").upper()             ###把接收的数据转变成大写字母
            conn.send(bytes(send_data,encoding="utf-8"))                ###发送数据,类型必须是bytes类型
        except Exception:
            continue
    conn.close()                    ###关闭连接

    客户端:

    import socket               ###导入socket模块
    
    s = socket.socket()             ###实例化socket对象
    server_info = ("127.0.0.1",12345)          
    s.connect(server_info)              ###连接服务端的地址和端口
    while True:
        send_data = input(">> ").strip()            ###客户端输入数据
        if send_data == "exit":break
        if len(send_data) == 0:continue
        s.send(bytes(send_data,encoding="utf8"))            ###客户端发送数据,类型必须是bytes类型
        recv_data = s.recv(1024)            ###客户端接收数据
        print(str(recv_data,encoding="utf8"))
    s.close()               ###关闭连接

    先执行服务端代码,后执行客户端代码,在客户端执行的结果如下:

    >> ls
    LS
    >> fuck
    FUCK
    >> haha
    HAHA
    >> exit
    
    Process finished with exit code 0

    代码解析:以上代码实现了最简单的socket通信,这里需要注意的是,accept()、recv()、send()方法都是阻塞类型,参数不能为空,假如参数为空,程序就会阻塞住,不能执行下面的代码。

     

    利用socket,我们可以实现在客户端类似ssh连接服务端一样,执行系统命令,下面是具体代码及讲解。

    服务端:

    import socket               ###导入socket模块
    import subprocess               ###导入系统命令模块
    
    s = socket.socket()             ###实例化socket对象
    
    server_addr = ("127.0.0.1",12345)
    
    s.bind(server_addr)             ###绑定地址和端口
    
    s.listen(1)             ###在拒绝连接前,最大接收挂起的客户端为1个
    
    while True:
        conn,addr = s.accept()          ###等待接收数据
        while True:
            try:
                recv_data = conn.recv(1024)             ###接收数据
                print(recv_data)
                result = subprocess.Popen(str(recv_data,encoding="utf8"),shell=True,stdout=subprocess.PIPE)     ###执行系统命令
                result_base = result
                res = result_base.stdout.read()         ###获取命令结果
                result_base.communicate()               ###得到命令返回值
                if result_base.returncode:              ###判断命令返回值
                    send_data = "cmd error"
                elif len(res) == 0:
                    send_data="None"
                else:
                    send_data=str(res,encoding="utf-8")
                send_data = bytes(send_data,encoding="utf8")
                len_send_data = bytes(len(send_data))          ###发送字节类型数据
                conn.send(send_data)                 ###发送字节类型数据
            except Exception:
                break
        conn.close()

     客户端和上个例子一样代码即可:

    import socket               ###导入socket模块
    
    s = socket.socket()             ###实例化socket对象
    server_info = ("127.0.0.1",12345)
    s.connect(server_info)              ###连接服务端的地址和端口
    while True:
        send_data = input(">> ").strip()            ###客户端输入数据
        if send_data == "exit":break
        if len(send_data) == 0:continue
        s.send(bytes(send_data,encoding="utf8"))            ###客户端发送数据,类型必须是bytes类型
        recv_data = s.recv(1024)            ###客户端接收数据
        print(str(recv_data,encoding="utf8"))
    s.close()               ###关闭连接

    先执行服务端,再执行客户端,客户端执行结果如下:

    >> ls
    socket_client.py
    socket_client1.py
    socket_client2.py
    socket_server.py
    socketclient_test.py
    socketserver_test.py
    test.py
    
    >> df -h
    Filesystem      Size   Used  Avail Capacity  iused    ifree %iused  Mounted on
    /dev/disk1     233Gi  173Gi   59Gi    75% 45438905 15542341   75%   /
    devfs          180Ki  180Ki    0Bi   100%      622        0  100%   /dev
    map -hosts       0Bi    0Bi    0Bi   100%        0        0  100%   /net
    map auto_home    0Bi    0Bi    0Bi   100%        0        0  100%   /home
    
    >> abcd
    cmd error

    附上socket模块的其他方法:

    socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

    参数一:地址簇

      socket.AF_INET IPv4(默认)
      socket.AF_INET6 IPv6

      socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

    参数二:类型

      socket.SOCK_STREAM  流式socket , for TCP (默认)
      socket.SOCK_DGRAM   数据报式socket , for UDP

      socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
      socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
      socket.SOCK_SEQPACKET 可靠的连续数据包服务

    参数三:协议

      0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

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

    2.socketserver 

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

    附上服务端代码:

    import socketserver         ###导入socketserver模块
    import subprocess           ###导入系统命令模块
    
    class MySocketServer(socketserver.BaseRequestHandler):      ###创建一个类继承socketserver.BaseRequestHandler
        def handle(self):                                       ###创建handle方法,必须叫handle,因为其父类有handle方法
            self.request.sendall(bytes("欢迎致电10086...",encoding="utf8"))     ###向所有客户端发送一条语句
            while True:
                data = self.request.recv(1024)                  ###接收数据
                str_data = data.decode()                        ###转码为字符串
                ret = subprocess.Popen(str_data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)       ###执行系统命令
                result = ret.stdout.read()  
                if not result:
                    result = ret.stderr.read()
                if len(result) == 0:
                    result = bytes("执行结果为空",encoding="utf-8")
                if len(str_data) == 0:
                    break
                print("[%s] says: [%s]" % (self.client_address,str_data))       ###打印客户端地址以及数据
                self.request.sendall(result)                ###发送数据
    
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(("127.0.0.1",8007),MySocketServer)     ###实例化socketserver.ThreadingTCPServer的对象,参数一是地址和端口,参数二是创建的类
        server.serve_forever()              ###执行对象的serve_forever方法,作用是循环监听

    客户端:

    import socket
    
    ip_port = ('127.0.0.1',8007)
    
    s = socket.socket()
    s.connect(ip_port)
    welcome_msg = s.recv(1024)
    
    print("from server: ",welcome_msg.decode())
    
    while True:
        send_data = input(">> ").strip()
        if len(send_data) == 0:continue
        s.send(bytes(send_data,encoding='utf8'))
    
        recv_data = s.recv(1024)
        print(str(recv_data,encoding="utf8"))
    
    s.close()

    客户端代码依旧是socket模块中的例子,先执行服务端代码,当两次及以上执行客户端代码时,发现输入的命令都能得到返回值,实现了并发的效果。

  • 相关阅读:
    BZOJ 1391: [Ceoi2008]order
    BZOJ 4504: K个串
    2019 年百度之星·程序设计大赛
    POJ 2398 Toy Storage (二分 叉积)
    POJ 2318 TOYS (二分 叉积)
    HDU 6697 Closest Pair of Segments (计算几何 暴力)
    HDU 6695 Welcome Party (贪心)
    HDU 6693 Valentine's Day (概率)
    HDU 6590 Code (判断凸包相交)
    POJ 3805 Separate Points (判断凸包相交)
  • 原文地址:https://www.cnblogs.com/Caesary/p/5640013.html
Copyright © 2011-2022 走看看