python基础知识(九)网络编程

python基础知识(九)网络编程

目录

(九)网络编程

9.1套接字(socket)

9.2黏包

9.3使用struct解决黏包 

9.4检验客户端的合法性

9.5socketserver


9.1套接字(socket)

基于文件类型的套接字家族

套接字家族的名字:AF_UNIX

基于网络类型的套接字家族

套接字家族的名字:AF_INET,AF_INET6被用于ipv6

基于TCP协议的socket

tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

#sever
import socket
sk = socket.socket()          #买手机
sk.bind(('127.0.0.1',8080))  #绑定手机号 #sk.bind(('ip','port端口')) 把地址(环回地址)绑定到套接字
sk.listen()          #监听链接
conn, addr = sk.accept() #接受客户端链接 connection 连接 address 地址
ret = conn.recv(1024)  #接收客户端信息
print(ret)       #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字

ret = conn.recv(1024)         # 对话(发送/接收)
print(ret.decode('utf-8'))   #汉字记得需要,添加编码格式utf-8

sk.close()        #关闭服务器套接字(可选)
#client
import socket
sk = socket.socket()           # 创建客户套接字
sk.connect(('127.0.0.1',8080))    # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024)         # 对话(发送/接收)

sk.send(bytes('你好',encoding='utf-8'))#汉字需要转码 encode()

print(ret)
sk.close()

基于UDP协议的socket

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

在启动服务之后只能被动的等待客户端发送信息

客户端发送消息的同时还会自带地址信息

消息回复的时候,不仅需要发送消息,还需要把对方的地址发送过来

#sever
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',8080))

msg, addr = sk.recvfrom(1024)
print(msg.decode('utf-8'))
sk.sendto(b'bye',addr)

sk.close()
#client
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8080)

sk.sendto(b'hello',ip_port)
msg,addr = sk.recvfrom(1024)
print(msg.decode('utf-8'))
sk.sendto(b'bye',addr)

sk.close()

9.2黏包

基于tcp协议实现的黏包

#sever
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen()
conn, addr = sk.accept()
while True:
    cmd = input('>>>')
    conn.send(cmd.encode('utf-8'))
    ret = conn.recv(1024).decode('utf-8')
    print(ret)

conn.close()
sk.close()
#client
import socket
import subprocess
sk = socket.socket()
sk.connect(('127.0.0.1', 8080))

while True:
    cmd = sk.recv(1024).decode('gbk')
    ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout = 'stdout:' + (ret.stdout.read()).decode('gbk')
    stderr = 'stderr:' + (ret.stderr.read()).decode('gbk')

    sk.send(stdout.encode('utf-8'))
    sk.send(stderr.encode('utf-8'))

sk.close()

基于udp协议实现的黏包

#sever
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1', 8090))
msg, addr = sk.recvfrom(1024)
while True:
    cmd = input('>>>')
    if cmd == 'q':
        break
    sk.sendto(cmd.encode('utf-8'), addr)
    msg, addr = sk.recvfrom(1024)
    print(msg.decode('utf-8'))
sk.close()
#client
import socket
import subprocess
sk = socket.socket(type=socket.SOCK_DGRAM)
addr = ('127.0.0.1', 8090)
sk.sendto(b'hh', addr)

while True:
    cmd, addr = sk.recvfrom(1024)
    ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout = 'stdout:' + (ret.stdout.read()).decode('gbk')
    stderr = 'stderr:' + (ret.stderr.read()).decode('gbk')
    sk.sendto(stdout.encode('utf-8'),addr)
    sk.sendto(stderr.encode('utf-8'),addr)

注意:只有TCP有粘包现象,UDP永远不会粘包

9.3使用struct解决黏包 

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

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

定制报头进行上传下载

#sever
import json
import struct
import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen()

conn, addr = sk.accept()
dic_len = conn.recv(4)  # 4个字节 数字的大小
dic_len = struct.unpack('i', dic_len)[0]
content = conn.recv(dic_len).decode('utf-8')  # 70
content_dic = json.loads(content)
if content_dic['operate'] == 'upload':
    with open(content_dic['filename'], 'wb') as f:
        while content_dic['filesize']:
            file = conn.recv(1024)
            f.write(file)
            content_dic['filesize'] -= len(file)
conn.close()
sk.close()
#client
import os
import json
import struct
import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 8080))

def get_filename(file_path):
    filename = os.path.basename(file_path)
    return filename

#选择 操作
operate = ['upload','download']
for num, opt in enumerate(operate, 1):
    print(num, opt)
num = int(input('请输入您要做的操作序号 : '))
if num == 1:
    '''上传操作'''
    file_path = input('请输入要上传的文件路径 : ')
    file_size = os.path.getsize(file_path)  # 获取文件大小
    file_name = get_filename(file_path)
    dic = {'operate': 'upload', 'filename': file_name, 'filesize':file_size}
    str_dic = json.dumps(dic).encode('utf-8')
    ret = struct.pack('i', len(str_dic))  # 将字典的大小转换成一个定长(4)的bytes
    sk.send(ret + str_dic)
    with open(file_path, 'rb') as f:
        while file_size:
            content = f.read(1024)
            sk.send(content)
            file_size -= len(content)
elif num == 2:
    '''下载操作'''
sk.close()

9.4检验客户端的合法性

利用hmac模块进行加密

#sever
import os
import socket
import hmac
def check_client(conn):
    secret_key = b'aa'  # 密钥
    send_str = os.urandom(32)
    conn.send(send_str)
    hmac_obj = hmac.new(secret_key,send_str)
    secret_ret = hmac_obj.digest()  #bytes类型
    if conn.recv(1024) == secret_ret:
        print('合法的客户端')
        return True
    else:
        print('非法的客户端')
        return False


sk = socket.socket()
sk.bind(('127.0.0.1',8090))
sk.listen()

conn,addr = sk.accept()
ret = check_client(conn)
while ret:
    inp = input('>>>')
    conn.send(inp.encode('utf-8'))
    msg = conn.recv(1024)
    print(msg.decode('utf-8'))
conn.close()
sk.close()
#client
import socket
import hmac
sk = socket.socket()
sk.connect(('127.0.0.1',8090))

recv = sk.recv(1024)
# 用和server端相同的手法对这个字符串进行摘要
secret_key = b'aa'  # 密钥
hmac_obj = hmac.new(secret_key,recv)
ret = hmac_obj.digest()
sk.send(ret)
msg = sk.recv(1024)
if msg:
    print(msg.decode('utf-8'))
    while True:
        inp = input('>>>')
        sk.send(inp.encode('utf-8'))
        msg = sk.recv(1024)
        print(msg.decode('utf-8'))
sk.close()

9.5socketserver

#sever
import socketserver
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        while 1:
            msg = self.request.recv(1024).decode('utf-8')
            print(msg)
            info = input('>>>')
            self.request.send(info.encode('utf-8'))
if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),  MyServer)
    server.serve_forever()
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9000))
while 1:
    msg = input('>>>').encode('utf-8')
    if msg == 'q':
        break
    sk.send(msg)
    ret = sk.recv(1024).decode('utf-8')
    print(ret)
sk.close()

猜你喜欢

转载自blog.csdn.net/zeroooorez/article/details/89967602