python网络编程(socket模块)

190 阅读5分钟

socker

一个简单的socket连接

# 服务端
import socket

# 1、买手机
phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)  # socket.socket(参数一(套接字家族可以使AF_UNIX或者AF_INET),参数2(套接字的类型是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM))

# 2、绑定手机卡
phone.bind(('127.0.0.1', 9090))  # IP和端口必须是一个元组

# 3、开机
phone.listen(5)  # 最大挂起的连接数

# 4、等电话链接
print('starting...')
conn, client_addr = phone.accept()  # 从套接字中获取客户端信息

# 5、收发消息
data = conn.recv(1024) # 单位:bytes, 2、1024表示最大接收1024个字节
print('客户端的消息',data)

conn.send(data.upper())

# 7、挂电话
conn.close()

# 8、关机
phone.close()

# 客户端
import socket
# 1、买手机
phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)  # socket.socket(参数一(套接字家族可以使AF_UNIX或者AF_INET),参数2(套接字的类型是面向连接的还是非连接分为SOCK_STREAM或SOCK_DGRAM))

# 2、拨号
phone.connect(('127.0.0.1', 9090)) # 发起链接

# 3、发,收消息
phone.send('hello'.encode('utf-8'))
data = phone.recv(1024)
print(data)

# 4、关闭
phone.close()

通讯循环

# 通讯循环的作用让服务端和客户端保持通讯,不会发一次通讯后就直接断开了

# 服务端
import socket

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)

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

phone.listen(5)

print('starting...')
conn, client_addr = phone.accept()
print(client_addr)  # 打印客户端IP和端口

while True:  # 通讯循环
    data = conn.recv(1024)
    conn.send(data.upper())

conn.close()

phone.close()

# 客户端
import socket

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 9090))

while True:
    msg = input('>>>:').strip()
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data)

phone.close()

socket通讯时遇到的bug客户端突然暴毙和客户端传入空值

# 服务端
import socket

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 端口复用

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

phone.listen(5)

print('starting...')
conn, client_addr = phone.accept()
print(client_addr)  # 打印客户端IP和端口

while True:
    # 解决服务端突然暴毙的解决方案
    try:    # 针对于win 
        data = conn.recv(1024)
    except ConnectionResetError:
        break
    # if not data:break # 针对于Linux
    print(data)
    conn.send(data.upper())

conn.close()

phone.close()


# 客户端
import socket

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 9090))

while True:
    msg = input('>>>:').strip()
    if not msg:continue     # 可解决客户端发生空值
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data)

phone.close()

实现服务端可以对多个客户端提供服务

# 链接循环的作用时服务端一直处于挂起的状态,但是不能实现并发的效果,客户端访问服务端时如果服务端已经有链接了客户端将进入等待的状态

# 服务端
import socket

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 端口复用

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

phone.listen(5) # 最大可以挂起5个客户端

print('starting...')
while True : # 链接循环
    conn, client_addr = phone.accept()
    print(client_addr)  # 打印客户端IP和端口

    while True:  # 通讯循环
        try:    # 针对于win
            data = conn.recv(1024)
        except ConnectionResetError:
            break
        # if not data:break # 针对于Linux
        print(data)
        conn.send(data.upper())

    conn.close()

phone.close()

# 客户端1-3
import socket

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 9090))

while True:
    msg = input('>>>:').strip()
    if not msg:continue     # 可解决客户端发生空值
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data)

phone.close()

模拟ssh远程执行命令


import json
import socket
import struct  # 报头打包模块
import subprocess

# res = struct.pack('i',128) # 参数1:类型的长度(i:4字节,l:8字节) , 参数2:整形
# struct.unpack('i',res) #  取出报头 参数1:打包类型 参数2:

phone = socket.socket(socket.AF_INET,
                      socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 端口复用

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

phone.listen(5)

print('starting...')
while True:
    # 1、接收命令
    conn, client_addr = phone.accept()
    print(client_addr)
    while True:
        try:
            cmd = conn.recv(1024)
        except ConnectionResetError or ConnectionAbortedError:
            break
        # 2、执行命令
        # 请注意编码的问题
        obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        # 3、把数据传给客户端
        # 第一步:制作固定长度的报头
        headr_dir = {
            'filename': 'a.txt',
            'md5': 'xxxx',
            'total_size': len(stdout) + len(stderr)
        }
        headr_json = json.dumps(headr_dir)
        headr_bytes = headr_json.encode()

        # 第二步:发送报头的长度
        conn.send(struct.pack('i', len(headr_bytes)))

        # 第三步:把报头发给客户端
        conn.send(headr_bytes)

        # 第四步:在发送真实数据
        conn.send(stdout)  # 根据tcp黏包的特性就不用字符串拼接了
        conn.send(stderr)
    # 返回给我客户端

    conn.close()

phone.close()

# 客户端
import json
import socket
import struct

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 9900))

while 1:
    # 发结果
    cmd = input('>>>:').strip()
    if not cmd: continue
    phone.send(cmd.encode('utf-8'))  # 客户端怎么编码的服务端就是这么解码

    # 收结果
    # 第一步先拿到报头的长度
    obj = phone.recv(4)
    header_size = struct.unpack('i', obj)[0]
    # 第二去 取出报头
    header_bytes = phone.recv(header_size)

    # 第三步:取出报头中的数据真实的描述信息
    header_json = header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    header = header_dic['total_size']

    # 第四步:取出报头中的数据真实数据
    total_size = header
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)

    print(recv_data.decode('gbk'))  # linux系统默认编码为utf-8 win系统默认编码gkb

phone.close()

实现文件传输

import json
import socket
import hashlib
import os
import struct  # 报头打包模块
import subprocess

# res = struct.pack('i',128) # 参数1:打包类型 , 参数2:整形
# struct.unpack('i',res) #  取出报头 参数1:打包类型 参数2:

def get(conn,cmds):
    filename = cmds[1]

    # 3、以读的方式读取文件内容,返回给客户端
    # 第一步:制作固定长度的报头
    headr_dir = {
        'file_name': ''.join(filename.split('\\')[-1:]),
        'md5': 'xxxxx',
        'file_size': os.path.getsize(filename)
    }

    print(headr_dir)
    headr_json = json.dumps(headr_dir)
    headr_bytes = headr_json.encode()

    # 第二步:发送报头的长度
    conn.send(struct.pack('i', len(headr_bytes)))

    # 第三步:把报头发给客户端
    conn.send(headr_bytes)

    # 第四步:在发送真实数据
    print(filename)
    with open(filename, 'rb') as f:
        for line in f:
            conn.send(line)

def run():
    phone = socket.socket(socket.AF_INET,
                          socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 端口复用

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

    phone.listen(5)

    print('starting...')
    while True:
        # 1、接收命令
        conn, client_addr = phone.accept()
        print(client_addr)
        while True:
            try:
                res = conn.recv(1024)
            except ConnectionResetError or ConnectionAbortedError:
                break
            # 2、解析命令,提取相应的命令参数
            cmds = res.decode('utf-8').split()
            if cmds[0] == 'get':
                get(conn,cmds)

        conn.close()

    phone.close()

if __name__ == '__main__':
    run()


# 客户端
import json
import socket
import struct

def get(phone):
    # 2、以的方式打开一个新文件,接收服务端的文件的内容写入客户端的新文件
    # 第一步先拿到报头的长度
    obj = phone.recv(4)
    header_size = struct.unpack('i', obj)[0]
    # 第二去 取出报头
    header_bytes = phone.recv(header_size)

    # 第三步:取出报头中的数据真实的描述信息
    header_json = header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    file_size = header_dic['file_size']
    file_name = header_dic['file_name']
    print(type(file_name))
    # 第四步:取出报头中的数据真实数据

    with open(file_name, 'wb') as f:
        print('开始写入')
        total_size = file_size
        recv_size = 0
        while recv_size < total_size:
            lien = phone.recv(1024)
            f.write(lien)
            recv_size += len(lien)
            print('总大小:%s 已下载大小:%s' % (total_size, recv_size))


def run():
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(('127.0.0.1', 9900))

    while True:
        # 1、发命令
        inp = input('>>>:').strip()
        if not inp: continue
        phone.send(inp.encode('utf-8'))  # 客户端怎么编码的服务端就是这么解码
        cmd = inp.split()
        print(cmd)
        if  cmd[0] == 'get':
            get(phone)

    phone.close()

if __name__ == '__main__':
    run()