socket编程加粘包现象

https://www.cnblogs.com/nulige/p/6235531.html

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

http://www.cnblogs.com/Eva-J/articles/8244551.html

http://www.cnblogs.com/linhaifeng/articles/6129246.html

 一.Socket

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()
  套接字的文件描述符
socket的应用(必会)

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

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

socket和file的区别:

  file模块是针对某个指定文件进行[打开][读写][关闭]

  socket模块是针对服务器端和客户端socket进行[打开][读写][关闭]

1.socket层

Socket是介于应用层和传输层之间

2.套接字的工作流程:

 3.TCP协议和UDP协议

(1).TCP可靠的,面向对象的协议(如:打电话),传输效率低全双工通信(发送缓存或者接收缓存),面向字节流.使用TCP的应用:Web浏览器;电子邮件,文件传输程序.

(2).UDP不可靠的,无连接的服务,传输效率高(发送前时延小),一对一,一对多,多对一,多对多,面向报文,尽最大努力服务,无拥塞控制.使用UDP的应用:域名系统(DNS);视频流;IP语音(VoIP).

二.套接字的初使用

1.上篇博客的最后

2.问题:有的人可能会遇到

解决方法:

#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)   #接收客户端信息
print(ret)              #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)

 3.基于UDP协议的socket

udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接

(1).简单使用

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字
服务端(server)
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
客户端(client)

 (2).具体示例:

import socket
ip_port=('127.0.0.1',8081)
udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udp_server_sock.bind(ip_port)

while True:
    qq_msg,addr=udp_server_sock.recvfrom(1024)
    print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
    back_msg=input('回复消息: ').strip()

    udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
server
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

qq_name_dic={
    '螳螂':('127.0.0.1',8081),
    '德邦':('127.0.0.1',8081),
    '剑圣':('127.0.0.1',8081),
    '剑豪':('127.0.0.1',8081),
}


while True:
    qq_name=input('请选择聊天对象: ').strip()
    while True:
        msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()
        if msg == 'q':break
        if not msg or not qq_name or qq_name not in qq_name_dic:continue
        udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])

        back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
        print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))

udp_client_socket.close()
client

 三.socket参数的详解

socket.socket(family = AF_INET,type = SOCK_STREAM,proto = 0,fileno = None)

创建socket对象的参数说明:

family

地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS.

(AF_UNIX域实际上是使用本地socket文件来通信)

type

套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一.

SOCK_STREAM是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送

SOCK_DGRAM是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播消息

proto 协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一
fileno

如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回.

与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的.

这可能有助于使用socket.close()关闭一个独立的插座

四.粘包

让我们基于TCP先制作一个远程执行命令的程序

这里引入一个subprocess模块

subprocess的目的就是启动一个新的进程并且与之通信

import subprocess

res=subprocess.Popen("dir",
                     shell=True,
                     stderr=subprocess.PIPE,
                     stdout=subprocess.PIPE)

print(res.stdout.read().decode("gbk"))

# 结果的编码是以当前所在的系统为准的,如果是Windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码,且只能从管道中读一次结果

 同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种现象就是粘包

1.基于TCP协议实现的粘包

from socket import *
import subprocess

ip_port=('127.0.0.1',8888)
BUFSIZE=1024

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

while True:
    conn,addr=tcp_socket_server.accept()
    print('客户端',addr)

    while True:
        cmd=conn.recv(BUFSIZE)
        if len(cmd) == 0:break

        res=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                         stdout=subprocess.PIPE,
                         stdin=subprocess.PIPE,
                         stderr=subprocess.PIPE)

        stderr=res.stderr.read()
        stdout=res.stdout.read()
        conn.send(stderr)
        conn.send(stdout)
TCP-server
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8888)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)

while True:
    msg=input('>>: ').strip()
    if len(msg) == 0:continue
    if msg == 'quit':break

    s.send(msg.encode('utf-8'))
    act_res=s.recv(BUFSIZE)

    print(act_res.decode('utf-8'),end='')
TCP-client

  上述程序是基于TCP的socket,在运行时会发生粘包

2.基于UDP协议实制作一个远程执行命令的程序

from socket import *
import subprocess

ip_port=('127.0.0.1',9000)
bufsize=1024

udp_server=socket(AF_INET,SOCK_DGRAM)
udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
udp_server.bind(ip_port)

while True:
    #收消息
    cmd,addr=udp_server.recvfrom(bufsize)
    print('用户命令----->',cmd)

    #逻辑处理
    res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
    stderr=res.stderr.read()
    stdout=res.stdout.read()

    #发消息
    udp_server.sendto(stderr,addr)
    udp_server.sendto(stdout,addr)
udp_server.close()
UDP-sever
from socket import *
ip_port=('127.0.0.1',9000)
bufsize=1024

udp_client=socket(AF_INET,SOCK_DGRAM)


while True:
    msg=input('>>: ').strip()
    udp_client.sendto(msg.encode('utf-8'),ip_port)
    err,addr=udp_client.recvfrom(bufsize)
    out,addr=udp_client.recvfrom(bufsize)
    if err:
        print('error : %s'%err.decode('utf-8'),end='')
    if out:
        print(out.decode('utf-8'), end='')
UDP-client

   上述程序是基于UDP的socket,在运行时永远不会发生粘包

 五.什么是粘包

须知:只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来

首先需要掌握一个socket收发消息的原理

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的.

1.TCP两种情况下回发生粘包:

(1).发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据流很小,会合到一起,产生粘包)

from socket import *
ip_port=('127.0.0.1',8080)

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)


conn,addr=tcp_socket_server.accept()


data1=conn.recv(10)
data2=conn.recv(10)

print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))

conn.close()
服务端(server)
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)


s.send('hello'.encode('utf-8'))
s.send('egg'.encode('utf-8'))
客户端(client)

(2).接收方不及时接收缓冲区的包,造成多个包接收(客户发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

from socket import *
ip_port=('127.0.0.1',8080)

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)


conn,addr=tcp_socket_server.accept()


data1=conn.recv(2) #一次没有收完整
data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的

print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))

conn.close()
服务端(server)
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)


s.send('hello egg'.encode('utf-8'))
客户端(client)

2.总结:

  (1).从表面上看,粘包问题主要是因为发送方和接收方的缓冲机制,TCP协议面向流通信的特点

  (2).实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

六.解决方案:

1.LOW版解决方案:

import socket,subprocess
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.bind(ip_port)
s.listen(5)

while True:
    conn,addr=s.accept()
    print('客户端',addr)
    while True:
        msg=conn.recv(1024)
        if not msg:break
        res=subprocess.Popen(msg.decode('utf-8'),shell=True,\
                            stdin=subprocess.PIPE,\
                         stderr=subprocess.PIPE,\
                         stdout=subprocess.PIPE)
        err=res.stderr.read()
        if err:
            ret=err
        else:
            ret=res.stdout.read()
        data_length=len(ret)
        conn.send(str(data_length).encode('utf-8'))
        data=conn.recv(1024).decode('utf-8')
        if data == 'recv_ready':
            conn.sendall(ret)
    conn.close()
服务端(server)
import socket,time
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if len(msg) == 0:continue
    if msg == 'quit':break

    s.send(msg.encode('utf-8'))
    length=int(s.recv(1024).decode('utf-8'))
    s.send('recv_ready'.encode('utf-8'))
    send_size=0
    recv_size=0
    data=b''
    while recv_size < length:
        data+=s.recv(1024)
        recv_size+=len(data)


    print(data.decode('utf-8'))
客户端(client)

为何low:

  程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

2.高阶解决方案:

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

(1).首先引入struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes

import struct

res=struct.pack("i","")

print(res)
print(len(res))


obj=struct.unpack("i",res)
print(obj[0])
struct
import json,struct
#假设通过客户端上传1T:1073741824000的文件a.txt

#为避免粘包,必须自定制报头
header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值

#为了该报头能传送,需要序列化并且转为bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输

#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度

#客户端开始发送
conn.send(head_len_bytes) #先发报头的长度,4个bytes
conn.send(head_bytes) #再发报头的字节格式
conn.sendall(文件内容) #然后发真实内容的字节格式

#服务端开始接收
head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度

head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
header=json.loads(json.dumps(header)) #提取报头

#最后根据报头的内容提取真实的数据,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)

3.使用struct解决粘包

借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字.因此可以利用这个特点来预先发送数据长度

发送时 接收时
先发送struct转换好的数据长度4字节 先接收4个字节使用struct转换成数字来获取要接收的数据长度
再发送数据 再按照长度接收数据
import socket
import json

sock = socket.socket()

# s.bind(address) 将套接字绑定到地址。
sock.bind(("127.0.0.1",8880))
sock.listen(5)

while True:
    print("server is working...")
    conn,addr = sock.accept()
    while True:
        data = conn.recv(1024).decode("utf-8")  # 接受套接字的数据。数据以字符串形式返回
        file_info = json.loads(data)     # 反序列化:将一个字符串格式的字典转换成一个字典
        print("file_info",file_info)    #{"action":"put","filename":"111.jpg","filesize":93605}

        # 文件信息
        action = file_info.get("action")
        filename = file_info.get("filename")
        filesize = file_info.get("filesize")

        conn.send(b"1")     # 接到数据返回1

        # 接收到文件数据
        with open("put/"+filename,"wb") as f:
            recv_data_length = 0    # 定义初始文件大小
            while recv_data_length < filesize:
                data = conn.recv(1024)  # 每次接收1024个字节
                recv_data_length += len(data)       # 初始大小+每次接收的文件大小
                f.write(data)   # 将每次接收的数据写入文件中
                print("文件的总大小: %s,已成功接收%s" % (filesize,recv_data_length))
        print("接收完成")
服务端(server)
import socket
import os
import json

sock = socket.socket()

# 连接到address处的套接字。
sock.connect(("127.0.0.1",8880))

while True:
    cmd = input("请输入命令: ") #put 222.gif
    action,filename = cmd.strip().split(" ")
    filesize = os.path.getsize(filename)

    file_info = {
        "action":action,    #命令
        "filename":filename,    #文件名
        "filesize":filesize,    #文件大小
    }
    file_info_json = json.dumps(file_info).encode("utf-8")  # 序列化将一个字典转换成字符串
    sock.send(file_info_json)

    code  = sock.recv(1024).decode("utf-8")
    if code == "1":
        # 发送文件数据
        with open(filename,"rb") as f:
            for line in f:
                sock.send(line)
    else:
        print("服务器异常")
客户端(client)

 

我们还可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struct将序列化后的数据长度打包成4个字节(4个自己足够用了)

发送时 接收时
先发报头长度 先收报头长度,用struct取出来
再编码报头内容然后发送 根据取出的长度收取报头内容,然后解码,反序列化
最后发真实内容 从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

phone.bind(('127.0.0.1',8080))

phone.listen(5)

while True:
    conn,addr=phone.accept()
    while True:
        cmd=conn.recv(1024)
        if not cmd:break
        print('cmd: %s' %cmd)

        res=subprocess.Popen(cmd.decode('utf-8'),
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        err=res.stderr.read()
        print(err)
        if err:
            back_msg=err
        else:
            back_msg=res.stdout.read()

        headers={'data_size':len(back_msg)}
        head_json=json.dumps(headers)
        head_json_bytes=bytes(head_json,encoding='utf-8')

        conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度
        conn.send(head_json_bytes) #再发报头
        conn.sendall(back_msg) #在发真实的内容

    conn.close()
服务端(定制稍微复杂一点的报头)
from socket import *
import struct,json

ip_port=('127.0.0.1',8080)
client=socket(AF_INET,SOCK_STREAM)
client.connect(ip_port)

while True:
    cmd=input('>>: ')
    if not cmd:continue
    client.send(bytes(cmd,encoding='utf-8'))

    head=client.recv(4)
    head_json_len=struct.unpack('i',head)[0]
    head_json=json.loads(client.recv(head_json_len).decode('utf-8'))
    data_len=head_json['data_size']

    recv_size=0
    recv_data=b''
    while recv_size < data_len:
        recv_data+=client.recv(1024)
        recv_size+=len(recv_data)

    print(recv_data.decode('utf-8'))
    #print(recv_data.decode('gbk')) #windows默认gbk编码
客户端(定制稍微复杂一点的报头)

4.实例应用(向服务器上传文件)

import socket
import struct
import json
import hashlib

sock = socket.socket()
sock.bind(("127.0.0.1",8800))
sock.listen(5)

while True:
    print("server is working...")
    conn,addr = sock.accept()
    while True:

        # 接收json的打包长度
        file_info_length_pack = conn.recv(4)
        file_info_length = struct.unpack("i",file_info_length_pack)[0]  # 解包,包中内容应为(61,)
        # a = struct.unpack("i",file_info_length_pack)
        # print(a)    # (61,)

        # 接收json字符串
        file_info_json = conn.recv(file_info_length).decode("utf-8")
        file_info = json.loads(file_info_json)

        action = file_info.get("action")
        filename = file_info.get("filename")
        filesize = file_info.get("filesize")

        # 循环接收文件
        md5 = hashlib.md5()
        with open("put/" + filename, "wb") as f:
            recv_data_length = 0
            while recv_data_length < filesize:
                data = conn.recv(1024)
                recv_data_length += len(data)
                f.write(data)
                # MD5摘要
                md5.update(data)
                print("文件总大小: %s,已成功接收%s" % (filesize,recv_data_length))
        print("接收成功")
        conn.send(b"OJBK")
        print(md5.hexdigest())
        md5_val = md5.hexdigest()
        client_md5 = conn.recv(1024).decode("utf-8")
        if md5_val == client_md5:
            conn.send(b'1')
        else:
            conn.send(b'0')
服务端(server)
import socket
import os
import json
import struct
import hashlib

sock = socket.socket()
sock.connect(("127.0.0.1",8800))

while True:
    cmd = input("请输入命令: ") # put 222.gif

    action,filename = cmd.strip().split(" ")
    filesize = os.path.getsize(filename)

    file_info = {
        "action":action,
        "filename":filename,
        "filesize":filesize,
    }
    file_info_json = json.dumps(file_info).encode("utf-8")

    # 报头
    ret = struct.pack("i",len(file_info_json))
    print(ret)
    # 发送file_info_josn打包之后的长度
    sock.send(ret)
    # 发送file_info_json字节串
    sock.send(file_info_json)
    # 发送文件数据
    md5 = hashlib.md5()
    with open(filename,"rb") as f:
        for line in f:
            sock.send(line)
            md5.update(line)

    data = sock.recv(1024)
    print(md5.hexdigest())
    md5_val = md5.hexdigest()
    sock.send(md5_val.encode("utf-8"))
    is_valid = sock.recv(1024).decode("utf-8")
    if is_valid == "1":
        print("文件完整")
    else:
        print("文件上传失败")
客户端(client)

七.关于hashlib模块的补充

import hashlib

md5=hashlib.md5()
md5.update(b"hello")
md5.update(b"yuan")

print(md5.hexdigest())
print(len(md5.hexdigest()))

#helloyuan:   d843cc930aa76f7799bba1780f578439
#             d843cc930aa76f7799bba1780f578439

分开加密和一起加密的结果一样

import hashlib

md5=hashlib.md5()

with open("ssh_client.py","rb") as f:
    for line in f:
        md5.update(line)

print(md5.hexdigest()) # f.read()
应用

八.socketserver模块的初次使用(多线程/多进程)

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

import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        # 字节类型
        while 1:
            # 针对window系统
            try:
                print("等待信息")
                data = self.request.recv(1024)  # 阻塞
                # 针对linux
                if len(data) == 0:
                    break
                if data == b'exit':
                    break
                response = data + b'SB'
                self.request.send(response)
            except Exception as e:
                break

        self.request.close()


# 1 创建socket对象 2 self.socket.bind()  3 self.socket.listen(5)
server=socketserver.ThreadingTCPServer(("127.0.0.1",8899),Myserver)

server.serve_forever()
服务端(server)
import socket

sk = socket.socket()

sk.connect(('127.0.0.1',8899))

while 1:
    name = input(">>>>:")
    sk.send(name.encode('utf-8')) # 字节

    response = sk.recv(1024) # 字节
    print(response.decode('utf-8'))
客户端(client)
posted @ 2018-09-04 17:42  骑驴老神仙  阅读(528)  评论(0)    收藏  举报