• python socket编程 day9



    首先说一下TCP/IP 协议,OS七层

    应用模型:

    socket 软件抽象层(不负责发送数据,真正发送数据的是socket后面的协议)

    socket

    socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

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

    socket和file的区别:

    • file模块是针对某个指定文件进行【打开】【读写】【关闭】
    • socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

    举个打电话的例子如图:

    import socket
    
    ip_port = ('127.0.0.1',9999)
    
    #买手机
    s= socket.socket() #封装好了TCP协议 ,生成套接字
    
    #买手机卡
    s.bind(ip_port) #绑定ip+协议+端口:用来唯一标示一个进程,ip_port必须是元组形式
    
    #开机
    s.listen(5) #定义可以挂起链接数(最大128个连接,队列等待)
    while True:
        #等待电话
        conn,addr = s.accept() #返回两个对象(conn=通信链路,addr是客户端ip+port)accetp(接受客户端请求过程是一个阻塞过程
        while True: #(用来接收一个连接重复发消息)
            try: #(捕捉客户端异常关闭ctrl+c)
                #收消息
                recv_data = conn.recv(1024) #接受1024字节 收消息,阻塞
                print(str(recv_data,encoding='utf-8'))
                if len(recv_data) == 0:break #客户端如果退出,服务端将受到空消息,退出
                #发消息
                send_data = recv_data.upper()
                conn.send(send_data)
            except Exception:
                break
    
        #挂电话
        conn.close()
    socket_server
    import socket
    ip_port = ('127.0.0.1',9999)
    
    #买手机
    s= socket.socket() #封装好TCP协议的
    
    #拨号
    s .connect(ip_port)
    while True:
        #发送消息
        send_data = input(">>:").strip()
        if send_data == 'exit':break
        if len(send_data) == 0:continue
    
        s.send(bytes(send_data,encoding='utf8'))  #2.0可以发字符串,3.0只能发字节
    
        #收消息
        recv_data = s.recv(1024)
        print(str(recv_data,encoding='utf-8'))
    #挂断
    s.close()
    socket_client

    解决粘包问题:

    服务端:1、send 数据长度

        4、recv 收到确认信息,开始下一步

    客户端:2、recv 获取数据长度

        3、send 发送确认信息

        5、recv 循环接收

    基于socket 实现类ssh远程执行命令

    import socket
    import subprocess
    
    ip_port = ('127.0.0.1',9999)
    s= socket.socket() #封装好了TCP协议 ,生成套接字
    s.bind(ip_port) #绑定ip+协议+端口:用来唯一标示一个进程,ip_port必须是元组形式
    s.listen(5) #定义可以挂起链接数(最大128个连接,队列等待)
    while True:
        conn,addr = s.accept() #返回两个对象(conn=通信链路)accetp过程是一个阻塞过程
        while True:
            try:
                recv_data = conn.recv(1024) #接受1024字节
                print(str(recv_data,encoding='utf-8'))
                if len(recv_data) == 0:break
            
                p = subprocess.Popen(str(recv_data,encoding='utf-8'),shell=True,stdout=subprocess.PIPE) #执行系统命令,Windows平台命令的标准输出是gbk编码,需要转换
                res = p.stdout.read() #获取标准输出
                if len(res) == 0:  #执行错误命令,标准输出为空
                    send_data = 'cmd error'
                else:
                    #解码 win系统的是gbk编码 gpk------>str------->utf8
                    send_data = str(res,encoding='gbk') 
                send_data1 = bytes(send_data,encoding='utf-8')
                ready_tag = 'Ready|%s' % len(send_data1)
                conn.send(bytes(ready_tag,encoding='utf-8')) #发送数据长度
                feedback = conn.recv(1024) #start 接受确认信息
                feedback = str(feedback,encoding='utf-8')
                if feedback.startswith("Start"):
                    conn.send(bytes(send_data,encoding='utf-8')) #发送数据
                #conn.send(bytes(send_data,encoding='utf-8')) #编码
            except Exception:
                break
       
        conn.close()
    sk_server
    import socket
    ip_port = ('127.0.0.1',9999)
    
    s= socket.socket() #封装好TCP协议的
    s .connect(ip_port)   #链接服务端,如果服务端已经有一个链接,那么挂起
    
    while True:     #基于connect建立的链接循环发消息
      
        send_data = input(">>:").strip()
        if send_data == 'exit':break
        if len(send_data) == 0:continue
    
        s.send(bytes(send_data,encoding='utf8'))  #2.0可以发字符串,3.0只能发字节
    
        #解决粘包问题
        ready_tag = s.recv(1024)       #收取带数据长度的字节Ready|len
        ready_tag = str(ready_tag,encoding='utf-8')
        if ready_tag.startswith('Ready'):     #Ready|9998
            msg_size = int(ready_tag.split('|')[-1])  #获取待接收数据长度
        start_tag = 'Start'
        s.send(bytes(start_tag,encoding='utf-8'))
    
        #基于已经收到的待接收数据长度,循环接收数据
        recv_size = 0
        recv_msg = b''
        while recv_size < msg_size:
            recv_data = s.recv(1024)
            recv_msg += recv_data
            recv_size += len(recv_data)
            print('MSG SIZE %s RECE SIZE %s ' % (msg_size,recv_size))
    
        print(str(recv_msg,encoding='utf-8'))
    
    s.close()
    sk_client

    总结: 

      1、python3.5的socket只能收发字节(2.7可以收发字符串)

      2、只客户端退出,服务器端不退出

      3、accept() 和recv()是阻塞的,阻塞的前提是基于链接正常(socket 除了accpet会阻塞,recv也会阻塞,如果发的是空就会阻塞)

      4、listen(n)n代表:能刮起的链接数,如果n=1,代表可以链接一个,挂起一个,第三个拒绝。

      5、服务端出现端口冲突:修改监听端口号

      6、import SocketServer #2.0版本

         import socketserver  #3.0版本

    socket 多并发

    import socketserver
    import subprocess
    
    class MyServer(socketserver.BaseRequestHandler): #必须继承此父类
        #方法名字必须是handle(父类里有handle方法但是是空,如果子类不写handle方法将会执行父类的handle)
        def handle(self):
            #conn = self.request
            self.request.sendall(bytes('欢迎致电10086,请输入0转人工服务',encoding='utf-8'))
            while True:
                data = self.request.recv(1024)
                if len(data) == 0:break
                print("[%s]says:%s"%(self.client_address,data.decode()))
                #self.request.sendall(data.upper())
                cmd = subprocess.Popen(data.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                cmd_res = cmd.stdout.read()
                if not cmd_res:
                    cmd_res = cmd.stderr.read()
                if len(cmd_res.strip()) == 0:
                    cmd_res = bytes("cmd no msg",encoding='utf-8')
                self.request.sendall(cmd_res)
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('0.0.0.0',8009),MyServer)
        server.serve_forever() #永远接收
    
    handle_server
    handle_server
    import socket
    ip_port = ('127.0.0.1',8009)
    
    #买手机
    s= socket.socket() #封装好TCP协议的
    
    #拨号
    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'))  #2.0可以发字符串,3.0只能发字节
    
        #收消息
        recv_data = s.recv(1024)
        print(str(recv_data,encoding='utf-8'))
    #挂断
    s.close()
    handle_client

    ftp上传实例:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author:Alex Li
    
    
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    #import SocketServer
    import socketserver,json
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            # print self.request,self.client_address,self.server
            #conn = self.request.sendall(用send和sendall都可以)
            self.request.sendall(bytes('欢迎致电 10086,请输入1xxx,0转人工服务.',encoding="utf-8"))
            while True:
                data = self.request.recv(1024)   #接收消息(动作,文件路径名,大小)
                if len(data) == 0:break       #如果为空退出
                print("data", data)
                print("[%s] says:%s" % (self.client_address,data.decode() ))
    
                task_data = json.loads(data.decode())  #接收到的字节信息转成字符串再转成字典
                task_action = task_data.get("action")  #获取动作
                if hasattr(self, "task_%s"%task_action):  #查看本程序里是否有这个函数成员
                   func = getattr(self,"task_%s" %task_action)  #获取函数
                   func(task_data)          #执行函数参数是字典信息,如果是tast_put那么执行tast_put(task_data)函数
                else:
                   print("task action is not supported",task_action)
    
        def task_put(self,*args,**kwargs):
            print("---put",args,kwargs)         
            filename = args[0].get('filename')
            filesize = args[0].get('file_size')
            server_response = {"status":200}
            self.request.send(bytes( json.dumps(server_response), encoding='utf-8'  ))#发送字典信息
            f = open(filename,'wb')
            recv_size = 0
            while recv_size < filesize: #接收字节小于文件字节
                data = self.request.recv(4096)
                f.write(data) #接收内容写进文件
                recv_size += len(data)
                print('filesize: %s  recvsize:%s' % (filesize,recv_size))
            print("file recv success")
            f.close()
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('0.0.0.0',8009),MyServer)
        server.serve_forever()
    ftp_server
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author:Alex Li
    
    import socket
    import os ,json
    ip_port=('192.168.11.150',8009)
    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   #是空重新输入
    
        cmd_list = send_data.split()  #将命令分割[put 123.txt]
        if len(cmd_list) <2:continue  #小于2个元素重新输入命令
        task_type = cmd_list[0]       #获取动作
        if task_type == 'put':         #上传
            abs_filepath = cmd_list[1]       #获取上传的文件
            if os.path.isfile(abs_filepath): #判断文件是否存在
            
                #os.stat(path))获取一个文件(夹)信息,返回值是一个元组包含10个属性
                file_size = os.stat(abs_filepath).st_size    #获取文件大小
                filename = abs_filepath.split("\\")[-1]      #打印文件名和大小
                print('file:%s size:%s' %(abs_filepath,file_size))
                msg_data = {"action":"put",
                            "filename":filename,
                            "file_size":file_size}
    
                s.send(bytes(json.dumps(msg_data),encoding="utf-8"))  #先转成字符串在转成字节发送
                server_confirmation_msg = s.recv(1024)                #接受服务器端发送的确认信息    
                confirm_data = json.loads(server_confirmation_msg.decode())
                if confirm_data['status'] ==200: #确认信息
    
                    print("start sending file ",filename)
                    f = open(abs_filepath,'rb') #字节方式打开
                    for line in f:
                        s.send(line) #一行行发送
    
                    print("send file done ")
    
            else:
                print("\033[31;1mfile [%s] is not exist\033[0m" % abs_filepath)
                continue
        else:
            print("doesn't support task type",task_type)
            continue
     
        recv_data=s.recv(1024)
        print(str(recv_data,encoding='utf8'))
    
    s.close()
    ftp_client

     http://www.cnblogs.com/wupeiqi/articles/5040823.html

  • 相关阅读:
    Protected和Default的区别
    将数组中负数放在正数前面
    java.io包和杯子测楼
    hadoop基础
    极限编程和JUnit
    接口和抽象类
    C# 中窗口AutoScaleMode属性
    计算机的自启动管理
    labview中的移位寄存器、循环隧道,自动索引隧道的区别
    发现C#winform编程中不常用的控件(一)<FlowLayoutPanel控件><拆分器控件Splitcontainer >
  • 原文地址:https://www.cnblogs.com/QL8533/p/5640355.html
Copyright © 2020-2023  润新知