0
点赞
收藏
分享

微信扫一扫

网络编程实操

技术只适用于干活 2022-01-13 阅读 175

文章目录

网络编程实操

socket 套接字编程

服务端

server = socket.socket()  # 默认就是基于网络的TCP传输协议  
server.bind(('127.0.0.1', 8080))  # 绑定ip和port  '127.0.0.1' 是本地回环地址       
server.listen(5)  # 半连接池            开机(过渡)
sock, address = server.accept()  # 监听   三次握手的listen态
print(address)  # 客户端地址
data = sock.recv(1024)  # 接收客户端发送的消息  
print(data)
sock.send(b'hello my big baby~~~')  # 给别人回话
sock.close()  # 挂电话
server.close()  # 关机

客户端

import socket
client = socket.socket()  # 买手机
client.connect(('127.0.0.1', 8080))  # 拨号
# 说话
client.send(b'hello big DSB DSB DSB!')
# 听他说
data = client.recv(1024)
print(data)
client.close()

在这里插入图片描述

通信循环以及代码优化

  • 上诉代码实现了简单的服务端和客户端交互,但是次数太少了,接下来我们给代码进行优化,能够频繁交流,并且能够交流

server 端

import socket

server = socket.socket()  # 默认就是基于网络的TCP传输协议  
server.bind(('127.0.0.1', 8080))  # 绑定ip和port  '127.0.0.1' 是本地回环地址       
server.listen(5)  # 半连接池            开机(过渡)
sock, address = server.accept()  # 监听   三次握手的listen态
print(address)  # 客户端地址
while True:
    data = sock.recv(1024)  # 接收客户端发送的消息  
    print(data.encode('utf8'))
    sock.send(data+b'123')
sock.close()  # 挂电话
server.close()  # 关机

client 端

import socket

client = socket.socket()  # 买手机
client.connect(('127.0.0.1', 8080))  # 拨号

while True:
    into_info = input('你说一句>>>:').strip()
    # 说话
    client.send(into_info.decode('utf8'))
    data = client.recv(1024)
    print(data.decode('utf8'))

    
client.close()

完成以上功能够,还是有一些问题需要我们去解决,例如:

1. 客户端校验消息不能为空
    if len(data)  == 0: continue    

2. 服务端添加兼容性代码
	if len(data) == 0: break

3. 服务端重启频繁报端口占用错误
	from socket import SOL_SOCKET, SO_REUSEADDR
	server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 在bind前加
4. 客户端异常关闭服务端报错的问题
	使用异常捕获
5. 服务端链接循环
	加一层循环,当客户端断开链接后,能够重新接受新的客户端ip
6. 半连接池
	server.listen(5)  
	设置可以等待的客户端数量

在这里插入图片描述

server 端

import socket
from socket import SOL_SOCKET, SO_REUSEADDR  # 3

server = socket.socket()  
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)   # 3
server.bind(('127.0.0.1', 8080))    
server.listen(5)    #  6
while True:   # 5
    sock, address = server.accept() 
    print(address) 
    while True:
        try :   # 4
            data = sock.recv(1024)  
            if len(data) == 0: break  # 2
            print(data.encode('utf8'))
            sock.send(data+b'123')
        except Exception as e:
        	print(e)  
sock.close()  
server.close()

client 端

import socket

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

while True:
    into_info = input('你说一句>>>:').strip()
    if len(into_info) == 0: continue  # 1
    client.send(into_info.decode('utf8'))
    data = client.recv(1024)
    print(data.decode('utf8'))

    
client.close()

黏包现象

在这里插入图片描述

黏包现象产生的原因

黏包现象的解决办法

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

server 端

import socket
import subprocess
import json
import struct

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

while True:
    sock, address = server.accept()
    while True:
        data = sock.recv(1024)  # 接收cmd命令
        command_cmd = data.decode('utf8')
        sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        res = sub.stdout.read() + sub.stderr.read()  # 结果可能很大
        # 1.制作报头
        data_first = struct.pack('i', len(res))
        # 2.发送报头
        sock.send(data_first)
        # 3.发送真实数据
        sock.send(res)
        
 
client   端

import socket
import struct

client = socket.socket()  # 买手机
client.connect(('127.0.0.1', 8080))  # 拨号

while True:
    msg = input('请输入cmd命令>>>:').strip()
    if len(msg) == 0:
        continue
    client.send(msg.encode('utf8'))
    # 1.先接收固定长度为4的报头数据
    recv_first = client.recv(4)
    # 2.解析报头
    real_length = struct.unpack('i',recv_first)[0]
    # 3.接收真实数据
    real_data = client.recv(real_length)
    print(real_data.decode('gbk'))

在这里插入图片描述

案例: 局域网内实现文件上传下载

server 端

import os
import json
import socket
from socket import SOL_SOCKET, SO_REUSEADDR
import common

server = socket.socket()
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(('192.168.11.43', 8080))    # 服务器ip地址和端口
server.listen(5)

data_path = r'./服务端视频' #  服务端路径
if not os.path.exists(data_path):
    os.mkdir('./服务端视频')
    
if not os.path.
# 获取文件名列表
file_name_list = os.listdir(data_path)



while True:
    sock, address = server.accept()
    print(address)
    while True:
        data = sock.recv(4)
        if not data: break
        # 判断是否从服务器下载资源
        if data == bytes(1):
            # 序列化文件列表
            list_json = json.dumps(file_name_list)
            # 传输文建列表
            common.info_into(sock,'文件列表', len(list_json))
            sock.send(list_json.encode('utf8'))

            # 获取用户选择文件信息
            choice_data = sock.recv(4)
            if not choice_data: break
            choice = int(choice_data.decode('utf8'))
            file_name = file_name_list[choice-1]
            # 拼接路径,获取文件大小
            res = common.read_file(data_path, file_name, sock)
            if res:
                break
        elif data == bytes(2):
            real_dict2 = common.parse_dict(sock)
            common.witer_file(file_name_list, real_dict2, data_path, sock)


client 端

import os
import json
import socket
import common

client = socket.socket()



choice_list = ['下载文件', '上传文件', '退出']

data_path = r'./本地视频'   # 本地文件路径
file_name_list = os.listdir(data_path)

while True:
    client.connect(('192.168.11.43', 8080))    # 服务器ip地址和端口
    res = common.print_list(choice_list)
    if not res: continue
    client.send(bytes(res))
    if res == 1:
        real_dict1 = common.parse_dict(client)
        # 接受文件列表
        life_list = client.recv(real_dict1.get('size'))
        # 获取文件列表信息
        real_file_list = json.loads(life_list)
        # 打印文件信息
        temp = common.print_list(real_file_list)
        if not temp : continue
        client.send(str(temp).encode('utf8'))
        real_dict2 = common.parse_dict(client)
        common.witer_file(file_name_list, real_dict2, data_path, client)


    elif res == 2:
        choice = common.print_list(file_name_list)
        if not choice: continue
        file_name = file_name_list[choice - 1]
        res = common.read_file(data_path, file_name, client)
    else:
        client.close()
        break

common 公用部分

import json
import struct
import os
import time


# 枚举出列表内信息
def print_list(mylist):
    for i, j in enumerate(mylist, 1):
        print('%s>>>>%s' % (i, j))
    choice = eval(input('请输入功能编号: ').strip())
    if choice not in range(1, len(mylist) + 1):
        print('请输入正确的编号!!!')
        return
    return choice


#  解析报头
def parse_dict(sock):
    # 接受报头信息
    recv_first = sock.recv(4)
    # 解析字典报头
    dict_length = struct.unpack('i', recv_first)[0]
    # 接收字典数据
    real_data = sock.recv(dict_length)
    # 解析字典(json格式的bytes数据 loads方法会自动先解码 后反序列化)
    return json.loads(real_data)


# 信息传输
def info_into(sock, name, size):
    # 定义一个字典数据
    data_dict = {
        'file_name': name,
        'size': size,
    }
    # 序列化字典
    data_json = json.dumps(data_dict)
    # 制作字典报头
    data_first = struct.pack('i', len(data_json))
    # 发送字典报头
    sock.send(data_first)
    # 发送字典数据
    sock.send(data_json.encode('utf8'))


# 写入文件
def witer_file(file_name_list, real_dict2, data_path, sock):
    for f_name in file_name_list:
        if real_dict2.get('file_name') == f_name:
            write_file_name = '(1)' + real_dict2.get('file_name')
            break
    else:
        write_file_name = real_dict2.get('file_name')
    write_file_path = os.path.join(data_path, write_file_name)
    recv_size = 0
    real_size = real_dict2.get('size')
    scale = 50
    print("正在下载".center(scale // 2, "-"))
    start = time.time()
    with open(write_file_path, 'wb') as f:
        while recv_size < real_size:
            data = sock.recv(1024)
            recv_size += len(data)
            f.write(data)
            i = (recv_size / real_size) * 50
            i = int(i)
            a = '#' * i
            b = '.' * (scale - i)
            c = (i / scale) * 100
            dur = time.time() - start
            print("\r{:^3.0f}%[{}->{}]{:.2f}s".format(c, a, b, dur),end='')
    print("\n" + "下载完成".center(scale // 2, '-'))

# 读出数据
def read_file(data_path, file_name, sock):
    file_path = os.path.join(data_path, file_name)
    file_size = os.path.getsize(file_path)
    recv_size = 0
    scale = 50
    try:
        # 传输文件
        info_into(sock, file_name, file_size)
        start = time.time()
        print("正在上传".center(scale // 2, "-"))
        with open(file_path, 'rb') as f:
            for line in f:
                sock.send(line)
                recv_size += len(line)
                i = (recv_size / file_size) * 50
                i = int(i)
                a = '#' * i
                b = '.' * (scale - i)
                c = (i / scale) * 100
                dur = time.time() - start
                print("\r{:^3.0f}%[{}->{}]{:.2f}s".format(c, a, b, dur), end='')
            print("\n" + "上传完成".center(scale // 2, '-'))
    except Exception as e:
        print(e)
        return True

在这里插入图片描述

扩展知识

在阅读源码的时候
	1.变量名后面跟冒号 表示的意思是该变量名需要指代的数据类型
    2.函数后更横杆加大于号表示的意思是该函数的返回值类型
举报

相关推荐

0 条评论