zoukankan      html  css  js  c++  java
  • day08 多线程socket 编程,tcp粘包处理

     复习下socket 编程的步骤:

    服务端:
     
    1 声明socket 实例
    server = socket.socket()  #括号里不写  默认地址簇使用AF_INET  即 IPv4       默认type 为 sock.SOCK_STREAM 即 TCP/IP 协议   
    2 绑定IP地址和端口
    server.bind(('localhost',9999))  #ip地址和端口  元组形式 ,端口为整数形式
     
    3 开始监听
    server.listen()
     
    4 进入阻塞状态,等待连接进入
    ######## 从这里开始可以加入循环,循环不同的连接 ,这样就可以接受不同的客户端的连接了#####
    #while True:
     
      # 获取客户端ip地址和端口  开启一个连接实例
     
     
    ##############  从这里开始循环,这样就可以循环收到客户端数据,但是只收这一个客户端 ###########
    ##客户端一断开,conn收到的就全为空数据,这样就进入了一个死循环,所以需要一个if 判断
    #while True:
    5 收数据
    data = conn.recv(1024)  #接收最大数据量, 官方建议最大8192  写整数据
    # 这里加入if 判断
    # if not data:
    #    break
    print(data)
     
    6 可以往回发送数据
    conn.send(data.upper()) #可以发送任何数据,这里把收到的变为大写
     
    7 server.close()
     
      
    客户端:
    1 声明socket 实例:
    client= socket.socket()
    2 连接服务端:
    client.connect(('server_ip',9999))   #连接服务端的ip及端口 元组形式 ,端口为整数形式
    3 发数据
    client.send(data)   #发送数据,任意数据
    # 4 收取服务端返回的数据
    #   recv_data = client.recv(1024)

    动态导入模块:

     1 实践:
     2 1 lib下的aa
     3 def test():
     4     print("hehe")
     5 class C(object):
     6     name = "yang"
     7     def __init__(self):
     8         self.name = 'shenyang'


    9 lib外的test 10 import importlib 11 dd = importlib.import_module('lib.aa') 12 dd.test() 13 print(dd.C().name)

     运行:

    断言:
    判断类型
     
    正确判断

     

    判断错误

    和 if  判断  一样 但是更简洁,一句话就OK 

    继续socket 编程:

     

    客户端虽然写的是收1024  但是不一定收到1024  只代表最多收到1024  即使服务端返回100个字节 也收
     
    客户端收完数据,收多少 要服务端先发一个要发多少的数据,客户端收到后,开始接收数据,直到收到指定大小的数据为止,不再接收
    实践:
    服务端
     1 #!/usr/bin/env python3
     2 # Author: Shen Yang
     3 import socket,os
     4 ip_address = '192.168.16.10'
     5 port = 8888
     6 bind_address = (ip_address,port)
     7 server = socket.socket()
     8 server.bind(bind_address)
     9 server.listen()
    10 while True:
    11     conn,addr = server.accept()
    12     while True:
    13         data = conn.recv(1024).decode()
    14         if not data:
    15             print("丢失连接")
    16             break
    17         print("这是来自",addr,data)
    18         cmd_res = os.popen(data).read()
    19         conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))
    20         conn.send(cmd_res.encode('utf-8'))
    21 server.close()

    客户端

     1 #!/usr/bin/env python3
     2 # Author: Shen Yang
     3 import socket
     4 ip_address = '192.168.16.10'
     5 # ip_address = '192.168.81.133'
     6 port = 8888
     7 conn_address = (ip_address,port)
     8 client = socket.socket()
     9 client.connect(conn_address)
    10 while True:
    11     cmd = input(":> ").encode('utf-8')
    12     if len(cmd) == 0:
    13         continue
    14     client.send(cmd)
    15     cmd_size = int(client.recv(1024).decode())
    16     print(cmd_size)
    17     recov_size = 0
    18     recov_data = b''
    19     while recov_size < cmd_size:
    20         data = client.recv(1024)
    21         #print(data)
    22         recov_size += len(data)
    23         #print(recov_size)
    24         recov_data += data
    25     else:
    26         print("收完了,大小",recov_size)
    27         print(recov_data.decode())
    28 client.close()

     上面的代码 在linux 发送 会出现粘包   客户端收到的是两个数据

    终极解决办法:
     
    服务端添加接收过程:

    客户端加发送过程:

    实践:
    ssh_server:
    #!/usr/bin/env python3
    import socket,os
    ip_address = '192.168.81.133'
    port = 8888
    bind_address = (ip_address,port)
    server = socket.socket()
    server.bind(bind_address)
    server.listen()
    while True:
        conn,addr = server.accept()
        while True:
            data = conn.recv(1024).decode()
            if not data:
                print("丢失连接")
                break
            print("这是来自",addr,data)
            cmd_res = os.popen(data).read()
            conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))
            ack = conn.recv(1024).decode()
            print(ack)
            conn.send(cmd_res.encode('utf-8'))
    server.close()

    ssh_client

     1 #!/usr/bin/env python3
     2 import socket
     3 ip_address = '192.168.81.133'
     4 port = 8888
     5 conn_address = (ip_address,port)
     6 client = socket.socket()
     7 client.connect(conn_address)
     8 while True:
     9     cmd = input(":> ").encode('utf-8')
    10     if len(cmd) == 0:
    11         continue
    12     client.send(cmd)
    13     cmd_size = int(client.recv(1024).decode())
    14     print(cmd_size)
    15     client.send("收到大小".encode('utf-8'))
    16     recov_size = 0
    17     recov_data = b''
    18     while recov_size < cmd_size:
    19         data = client.recv(1024)
    20         #print(data)
    21         recov_size += len(data)
    22         #print(recov_size)
    23         recov_data += data
    24     else:
    25         print("收完了,大小",recov_size)
    26         print(recov_data.decode())
    27 client.close()

    传文件:
     
    server 端逻辑:

     

    使用 os.stat  获取文件详细信息,发送给客户端

    另外一个解决粘包的方法:

    只收指定大小的数据  永远不可能粘包

     

    实践:
     ftp_server.py
     1 #!/usr/bin/env python3
     2 import socket,os,hashlib
     3 ip_address = '192.168.81.133'
     4 port = 8888
     5 bind_address = (ip_address,port)
     6 server = socket.socket()
     7 server.bind(bind_address)
     8 server.listen()
     9 while True:
    10     conn,addr = server.accept()
    11     while True:
    12         print("等待新链接")
    13         data = conn.recv(1024)
    14         if not data:
    15             print("丢失连接")
    16             break
    17         print("这是来自",addr,data)
    18         cmd,file_name = data.decode().split()
    19         if os.path.isfile(file_name):
    20           m = hashlib.md5()
    21           f = open(file_name,'rb')
    22           file_size = os.stat(file_name).st_size
    23           conn.send(str(file_size).encode())
    24           ack = conn.recv(1024).decode()
    25           print("确认:",ack)
    26           for line in f:
    27             m.update(line)
    28             print("sending...")
    29             conn.send(line)
    30           f.close()
    31           conn.send(m.hexdigest().encode())
    32         print("发送完毕")
    33 server.close()

    ftp_client.py 

     1 #!/usr/bin/env python3
     2 import socket,hashlib
     3 ip_address = '192.168.81.133'
     4 port = 8888
     5 conn_address = (ip_address,port)
     6 client = socket.socket()
     7 client.connect(conn_address)
     8 while True:
     9     cmd = input("input your cmd:> ").strip()
    10     if len(cmd) == 0:
    11         continue
    12     if cmd.startswith("get"):
    13       client.send(cmd.encode('utf-8'))
    14       file_name = cmd.split()[1]
    15       server_response = client.recv(1024)
    16       print("收到回应:",server_response)
    17       client.send("收到".encode('utf-8'))
    18       total_file_size = int(server_response.decode())
    19       print("总的",total_file_size)
    20       f = open(file_name + '.new','wb')
    21       recov_file_size = 0
    22       m = hashlib.md5()
    23       while recov_file_size < total_file_size:
    24           if total_file_size - recov_file_size > 1024:
    25              size = 1024
    26           else:
    27              size =  total_file_size - recov_file_size
    28              print("最后收",size)
    29           #print("收数据...")
    30           data = client.recv(size)
    31           m.update(data)
    32           recov_file_size += len(data)
    33           f.write(data)
    34       else:
    35           print("收完了,大小",recov_file_size,total_file_size)
    36           f.close()
    37           print("服务器端发送的原MD5",client.recv(1024).decode())
    38           print("自己算出来的最终MD5",m.hexdigest())
    39 client.close()

    开始学习SocketSever
     
    SocketSever  是对socket 的再封装
     
    最常用的是 TCP 和 UDP 

    继承关系:

     创建socket server 步骤:

     

     下面是重写的handle:

    跟客户端所有的交互都是在handle 里处理的
    每一个客户端请求都是实例化一个handle

     

    继续修改handle
    增加循环:
    并增加判断客户端退出:

     

    实践:
    server:
     1 import socketserver
     2 class MyTCPHandler(socketserver.BaseRequestHandler):
     3     def handle(self):
     4         while True:
     5             try:
     6                 self.data = self.request.recv(1024).strip()
     7                 print("来自",self.client_address)
     8                 print(self.data)
     9                 self.request.send(self.data.upper())
    10             except ConnectionResetError as e:
    11                 print("客户端退出",e)
    12                 break
    13 if __name__ == "__main__":
    14     HOST,PORT="localhost",9999
    15     server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    16     server.serve_forever()

    client:

     1 import  socket
     2 client = socket.socket() #声明socket类型,同时生成socket连接对象
     3 #client.connect(('192.168.16.200',9999))
     4 client.connect(('localhost',9999))
     5 while True:
     6     msg = input(">>:").strip()
     7     if len(msg) == 0:continue
     8     client.send(msg.encode("utf-8"))
     9     data = client.recv(10240)
    10     print("recv:",data.decode())
    11 client.close()

    上面的并不支持多并发
    到现在为止,只是写了一个一对一的服务器
    下面写入多并发:
    只改一个地方:
    1 使用多线程 来实现

    2 使用多进程实现:  (windos 下不行,不支持,但是linux  绝得好使)

     
     
     
    介绍 socketserver的一些方法:
     
    1 返回文件描述符  (系统自己调用,我们一般用不到)

    2 处理单个请求,一般也用不到

    3 一直收请求,直到收到一个明确的shutdown   (每0.5秒检测是否给我发了shutdown 的信号)

    收到shutdown 后调用 service_action() 清理垃圾僵尸进程

     

    4 告诉 server_forever() 关闭

    5 地址的重用,不需要等待tcp断开

    普通的sock server
    server = socket.socket() #获得socket实例
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  #重用地址
    请求的三个过程,都可以自定义:
    1 setup
    2 handle
    3 finish

     

  • 相关阅读:
    设置圆形头型并且可以点击
    不可忽略的知识点
    【Java基础】03_Java常用API
    常用键盘功能键和快捷键以及DOS命令
    【Excel 2013 数据透视表 学习】一、创建数据透视表
    【Java基础】05_异常&File
    【Java基础】04_集合框架
    【设计模式之禅】第2章 里氏替换原则
    【设计模式之禅】第1章 单一职责原则
    Eclipse中快捷键的使用
  • 原文地址:https://www.cnblogs.com/yangever/p/5876115.html
Copyright © 2011-2022 走看看