zoukankan      html  css  js  c++  java
  • Python的socket模块与交互式指令

    socket简介

    在编程的过程中,我们需要使用网络编程,这时我们不得不和网络通信的底层基础打交道了.我们必须让自己传输的数据符合网络通信的基本协议,即TCP/IP协议,但是网络通信协议本身很复杂.我们不可能在编程的过程中还自己去对数据进行封包处理,这是便出现了socket帮助我们处理相关的数据传输,在其他语言里也可以使用socket帮我们处理相关问题.

    socket本质就是一组接口,是在应用层与TCP/IP协议族通信中间的一个抽象层,庞大复杂的TCP/IP协议族我们便可以不用过多关注,而只需要通过socket提供的接口就可以相互连接.

    socke模块中的基本命令

    s.bind()    在服务端绑定(主机,端口号)到套接字
    s.listen()  开始TCP监听
    s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来,收到的是元组形式,包含内容和地址
    s.connect()     主动初始化TCP服务器连接
    s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    s.recv()            接收TCP数据
    s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    s.recvfrom()        接收UDP数据
    s.sendto()          发送UDP数据
    s.getpeername()     连接到当前套接字的远端的地址
    s.getsockname()     当前套接字的地址
    s.getsockopt()      返回指定套接字的参数
    s.setsockopt()      设置指定套接字的参数
    s.close()           关闭套接字
    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout()      设置阻塞套接字操作的超时时间
    s.gettimeout()      得到阻塞套接字操作的超时时间
    s.fileno()          套接字的文件描述符
    s.makefile()        创建一个与该套接字相关的文件
    

    TCP的服务端和客户端示例

    TCP服务端

    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ##网络编程需要调用AF_INET,而SOCK_STREAM代表数据流,即TCP方式
    phone.bind(('192.168.1.101',9000))
    ##绑定IP地址和端口,必须是元组
    phone.listen(5)
    ##设置监听数量
    while True:
        conn, addr = phone.accept()
        ##得到内容对象和地址
        while True:
            try:  ##异常处理特殊情况
                feedback = conn.recv(1024)
                ##得到内容对象传来的值,里面规定字节数量
                print(feedback.decode("UTF-8"))
                ##编译,因为只有被编译的字符才能被传输
                msg = input(">>:")
                conn.send(msg.encode("UTF-8"))
                ##发送
            except Exception:
                break
    
    conn.close() ##关闭连接了的对象
    phone.close() ##关闭服务器
    

    TCP客户端

    import socket
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect_ex(('192.168.1.101',9000)) ##连接对象
    while True:
        msg = input(">>:")
        phone.send(msg.encode("UTF-8")) ##发送消息
        feedback = phone.recv(1024) ##返回消息
        print(feedback.decode("UTF-8"))
    phone.close() ##关闭对象
    

    问题

    可能在多次开启关闭服务器的时候,会出现IP地址已经被使用了的情况,我们可以用以下方法解决:

    phone = socket(AF_INIT, SOCKET_STREAM)
    phone.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    phone.bind(('192.168.1.101',9000))
    

    UDP的服务端和客户端示例

    UDP的服务端

    from socket import *
    ip_addr = ('192.168.1.101',9000)
    buff_size = 1024
    udp_server = socket(AF_INET, SOCK_DGRAM)
    ##SOCK_DGRAM表示UDP方式
    udp_server.bind(ip_addr)
    ##绑定IP地址和端口
    while True:
        data, addr = udp_server.recvfrom(buff_size)
        ##受信息
        print(data.decode("utf-8"))
        udp_server.sendto(data.upper(), addr)
        ##发信息
    

    UDP的客户端

    from socket import *
    
    ip_addr = ('192.168.1.101',9000)
    buff_size = 1024
    udp_client = socket(AF_INET, SOCK_DGRAM)
    while True:
        msg = input(">>:")
        udp_client.sendto(msg.encode("utf-8"), ip_addr)
        ##不需要绑定,只需要传一个地址
        date, addr = udp_client.recvfrom(buff_size)
        print(date.decode("utf-8"))
    

    subprogress模块

    
    

    TCP和UDP比较

    TCP UDP
    传输方式 数据流(STREAM) 数据报(DGRAM)
    导致问题 粘包 丢包
    接待对象 一个 多个
    连接 需要连接线 不需要连接线

    TCP是以数据流的方式,在数据很多时,会多次地不分内容关联性地传输给对方内核态缓存,当用户态缓存来取数据时,并不知道数据的开头和结尾,那么就出现了粘包现象.因此粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的,所以通过TCP方式不能发送空.

    然而,UDP方式是以信息为单位传输数据,在数据里面包含了开头和结尾的界限,因此不会出现粘包的现象,但是当传输来的数据被储存在内核态缓存里面时,如果应用态缓存没有一次性的取完数据,那么数据就会被丢弃

    解决了粘包现象的远程指令程序

    客户端

    from socket import *
    
    ip_port = ('127.0.0.1', 9000)
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect_ex(ip_port)
    while True:
        command = input(">>:")
        if not command: continue
        client.send(command.encode("utf-8"))
        feedback_of_length = int(client.recv(1024).decode("gbk"))  #先得到数据的长度
        client.send("OK".encode("gbk"))   #并且发送可以传输了的命令
        all_data = b""  
        data_length = 0
        while data_length < feedback_of_length:
            all_data += client.recv(1024)  #循环地把数据联结在一起
            data_length = len(all_data)   
        last_date = all_data.decode("gbk")  ##最后解码
        print(last_date)
        
    client.close()
    

    服务端

    from socket import *
    import subprocess
    
    ip_port = ('127.0.0.1', 9000)
    buff_size = 5
    
    server = socket(AF_INET, SOCK_STREAM)
    server.bind(ip_port)
    server.listen(buff_size)
    while True:
        conn, addr = server.accept()
        while True:
            try:
                cmd = conn.recv(1024)
                if not cmd: continue
                res = subprocess.Popen(cmd.decode("utf-8"), shell=True,
                                       stderr = subprocess.PIPE,
                                       stdin = subprocess.PIPE,
                                       stdout = subprocess.PIPE
                )
                err = res.stderr.read()
                if not res:
                    get_res = "Run Perfectly"
                elif err:
                    get_res = err
                else:
                    get_res = res.stdout.read()
                length_of_res = len(get_res)  #发送数据长度
                conn.send(str(length_of_res).encode("gbk")) 
                last_info = conn.recv(1024)   #得到是否传输的指令
                if last_info.decode("utf-8") == "OK":
                     conn.send(get_res)
            except Exception:
                break
    

    socketserver实现并发

    TCP版服务端

    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):  ##必须要继承这个父类
    	def handle(self):  ##必须要重新定义这个方法
    		while True:
    			try:
    				data = self.request.recv(1024)  ##recv就相当于conn了
    				if not data: break
    				print(data.decode("utf-8"), self.client_address) ##只有data和client_address这两个量 
    				self.request.sendall(data)  
    			except Exception:
    				break
    
    
    if __name__ == "__main__":
    	s = socketserver.ThreadingTCPServer(('192.168.1.101',9001), MyServer)  ##实例化一个对象
    	s.serve_forever()  ##永远运行
    
    

    UDP版服务端

    import socketserver
    
    class MyServer(socketserver.BaseRequestHandle):
    	def handle(self):
            ##(b'ad', <socket.socket fd=444, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0,
            ##   laddr=('192.168.1.101', 8081)>)  request是一个元组
    		print(self.request[0].decode("utf-8"))
    		self.request[1].sendto(self.request[0], self.client_address)
    
    if __name__ == "__main__":
    	s = socketserver.ThreadingUDPServer(('192.168.1.101',8080), MyServer)
    	s.server_forever()
    
  • 相关阅读:
    Maven入门详解
    (二)从分布式一致性谈到CAP理论、BASE理论
    (一)从集中式到分布式
    四种MySQL存储引擎
    日期函数、时间函数总结(MySQL 5.X)
    SQL函数说明大全
    Java虚拟机1:什么是Java
    Java设计模式1:设计模式概论
    Linux概述
    Android 使用SharedPreference来进行软件配置的存取
  • 原文地址:https://www.cnblogs.com/MingleYuan/p/10628505.html
Copyright © 2011-2022 走看看