zoukankan      html  css  js  c++  java
  • 0911 Socket网络编程

    1.实现ftp上传、下载功能

    1.1 循环接收数据直到接收完毕

    server端接收client发送的命令(比如说ifconfig),然后server端将命令执行结果反馈给客户端,这时候有个问题,server端是一次性的把数据发给client了,但是client怎么接收全部呢?比如client.recv(1024)调大接收的值行吗?增加接收的次数行吗?

    调大接收的值,多大合适,每次server端发送的数据大小都不同;

    增加接收的次数,那增加到接收几次合适呢,也是不定值。

    循环接收buffer缓冲里的数据,一直到buffer里没有数据,这样也不行,因为buffer里可能存储着好多条命令的执行结果,一次性接收过来,显示在client端可能就是杂乱的数据了。除非server和client是一对一的,而且是只能发一条命令接收一条结果再发送一条命令,这样单线程的走。

    所以解决办法是,server在发送数据前先将要发的数据总大小发送给client,然后client接收这个总大小的字节即可接收完全部数据。

    #!/usr/bin/env python
    #coding:utf-8
    
    
    import socket,os
    
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(('localhost',9543))
    server.listen()
    
    while True:
        conn,addr = server.accept()
        while True:
            data = conn.recv(10240)
            if not data:break
            result = os.popen(data.decode()).read()
            if result == '':    #命令不存在的话result会为空
                result = 'command not found.'
    
            conn.send(str(len(result.encode('UTF-8'))).encode('UTF-8'))  #这里把result.encode(),是因为如果result里包含中文的话,会有点问题。
    #a = '期望'
    #len(a)   结果是2,计算的是字符数量
    #len(a.encode())  结果是6,计算的是字节数量。
    #发过去的大小,一个中文算一个数量,client接收到后,是按字节计算的,所以一个中文算3个数量,为了统一,就在发送长度的时候直接encode一下。
            conn.recv(1024)
            conn.send(result.encode('UTF-8'))
    server.close()
    
    
    import socket
    
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    client.connect(('localhost',9543))
    
    while True:
        u_input = input('>')
        if len(u_input) == 0:continue
        client.send(u_input.encode('UTF-8'))
        data_size = client.recv(1024).decode()
        client.send('OK'.encode())
        receive_size = 0
        print(data_size)
        print(type(data_size))
        while receive_size != int(data_size):    #只要已接收的字符小于server发送过来的字符数量,就一直接收。#如果server端发送字节长度时没有encode,同时发送的内容包含中文,当内容接收完毕后,就会出现receive_size 大于 data_size(包含中文越多相差值越大),因为一个中文在data_size那算一个数量,但是在receive_size那算三个数量。
            data = client.recv(1024)
            receive_size += len(data)
            print(data.decode())
        else:
            print('total length is :',receive_size)
        
    client.close()

    1.2 粘包的概念与解决

    server:
    conn.send('123'.encode())
    conn.send('456'.encode())
     server端连续发送两个数据
    
    client:
    client.recv(1024)
    client.recv(1024)
     client端接收server发送的两个数据
    
    因为server端的两次发送时紧挨着,所以在windows上可能出现粘包,在linux上肯定会出现粘包。
    粘包就是把两次send的内容一起放到了buffer里,client第一次recv时就能取到所有数据,就会client收到的结果造成混乱。
    
    #解决粘包:
    #方法一,不推荐:可以在两次send中间插一个time.sleep(0.5),这样两个包就会分开发了,不会黏在一起;但是每次都会停顿0.5秒,能明显感觉到延迟;比如股票这种强调速度的应用,用sleep根本不行。
    
    #方法二:在两次send中间插入一个recv
    server:
    conn.send(str(len(result.encode('UTF-8'))).encode('UTF-8'))
    conn.recv(1024)     #接收client一个消息后,再继续发送第二个send。
    conn.send(result.encode('UTF-8'))
    
    client:
    data_size = client.recv(1024).decode()
    client.send('OK'.encode())   #接收到第一条内容后,给server发送一个消息,让server代码继续执行
    data = client.recv(1024)
    
    #方法三:
    client端只接收该接收的部分,比如server给client发了一个文件(大小为2048字节),紧接着又发了一个md5值(32字节),那客户端可以先client.recv(2048),然后client.recv(32),这样肯定不会收错了,需要注意的是如果内容包含中文,必须先对内容编码(encode)再len计算内容长度。

     1.3 下例示范一个简单的ftp应用

    import socket
    import time
    import hashlib
    import os
    
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(('localhost',956))
    server.listen()
    
    while True:
        conn,addr = server.accept()
        while True:
            cmd,filename = conn.recv(1024).decode().split()
            if len(cmd) == 0 or len(filename) == 0: #命令或文件为空就退出
                break
            if os.path.isfile(filename): #判断是否存在文件
                filesize = os.stat(filename).st_size   #获取文件长度
                print(filesize)
                conn.send(str(filesize).encode())
                conn.recv(1024)
                m = hashlib.md5()
                with open(filename) as f:
                    for line in f:
                        m.update(line.encode())    #计算一行md5
                        conn.send(line.encode())
                server_md5 = m.hexdigest()     #获取最终的md5值
                conn.send(server_md5.encode())  
    server.close()
    
    
    
    
    
    import socket
    import hashlib
    
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    client.connect(('localhost',956))
    
    while True:
        u_input = input('>')
        if len(u_input) == 0:
            continue
        cmd,filename = u_input.split()
        if cmd.startswith('get'):
            client.send(u_input.encode())
            file_size = int(client.recv(1024).decode())   #接收文本大小
            print(file_size)
            client.send('ok'.encode())
            received_size = 0
            m = hashlib.md5()
            with open(filename + '.new','w') as f: 
                while received_size < file_size:
                    if file_size - received_size > 1024:     
                        size = 1024
                    else:
                        size = file_size - received_size     #如果剩余需要接收的字节小于1024,就只接收剩余大小的字节,防止接收到粘包数据。
                    data = client.recv(size)
                    received_size += len(data)         #将已接收的大小求和
                    m.update(data)
                    f.write(data.decode())
                else:
                    print('receitotal size is :%s' % received_size)
                    client_md5 = m.hexdigest()
            
            server_md5 = client.recv(1024).decode()
            
            print('client_md5:%s' % client_md5)
            print('server_md5:%s' % server_md5)
        else:
            print('get please.')
            continue
    client.close()

     2.SocketServer

    socket很好用,但是无法实现并发,所以就有了SocketServer,SocketServer是对socket的再封装,可实现网络并发处理。

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

    1. First, you must create a request handler class by subclassing the BaseRequestHandlerclass and overriding its handle() method; this method will process incoming requests.   
    2. Second, you must instantiate one of the server classes, passing it the server’s address and the request handler class.
    3. Then call the handle_request() orserve_forever() method of the server object to process one or many requests.
    4. Finally, call server_close() to close the socket.

    示例代码:

  • 相关阅读:
    python学习:字符编码与转码
    python学习:文件操作
    python学习:基本运算符
    python学习:列表、元组、字典、集合
    python学习:基础知识
    linux常用命令
    hadoop手动安全模式
    System.getProperty("user.dir")的理解
    如何获取SpringBoot项目的applicationContext对象
    spring无法注入bean
  • 原文地址:https://www.cnblogs.com/fuckily/p/5861974.html
Copyright © 2011-2022 走看看