socket 相关

解决粘包问题:
  服务端:先发送报头的长度,再发送报头的内容,最后才是内容
  客户端:先先接收报头的长度,再接收报头内容(有必要的时候报头的内容都需要循环接收),然后解析报头的内容获取结果内容的信息再收取结果内容。
服务端代码:
# -*- coding: utf-8 -*-
import json
import socket
import struct
import subprocess

'''
Socket套接字方法
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

family(socket家族)
socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

socket type类型
socket.SOCK_STREAM #for tcp
socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET #废弃了
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)

proto=0 请忽略,特殊用途
fileno=None 请忽略,特殊用途
'''
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 如果端口已经被使用,则回收再用
phone.bind(('localhost', 7001))  # 0-65535:0-1024给操作系统使用
phone.listen(5)
print('服务器已经启动。。。。。')
while 1:  # 循环连接到服务端,不过得等前一个关掉后一个才能上
    conn, addr = phone.accept()
    print('有客户端连接上来了!')
    print(conn, addr)
    if_conn = 1
    while 1:  # 循环通信
        try:  # try except是一个客户端端断开的时候,就断开此次的服务,准备开始等待下一个
            # windows 是try的方式,linux判断if not data:break即可!
            # 1.收命令
            data = conn.recv(1024)
            # 要注意的是,客户端服务端的收发命令都是字节类型
            # print(data.decode('utf-8'))

            # 2.执行命令,拿执行结果
            # stdout是正确执行的结果,stderr是报错的内容
            res = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            # 分别读取
            stdout = res.stdout.read()
            stderr = res.stderr.read()

            # 3、把命令的结果返回给客户端
            # 第一步:制作固定长度的报头
            header_dic = {
                'filename': 'a.txt',
                'md5': 'xxdxxx',
                'total_size': len(stdout) + len(stderr)
            }

            header_json = json.dumps(header_dic)

            header_bytes = header_json.encode('utf-8')

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

            # 第三步:再发报头
            conn.send(header_bytes)

            # 第四步:再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:  # 适用于windows操作系统
            break
    conn.close()

phone.close()
View Code

  客户端

# -*- coding: utf-8 -*-
import json
import socket
import struct

'''
Socket套接字方法
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

family(socket家族)
socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

socket type类型
socket.SOCK_STREAM #for tcp
socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET #废弃了
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)

proto=0 请忽略,特殊用途
fileno=None 请忽略,特殊用途
'''
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

phone.connect(('127.0.0.1', 7001))

while True:
    # 1、发命令
    cmd = input('>>: ').strip()  # ls /etc
    if cmd:
        phone.send(cmd.encode('utf-8'))

        # 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)
        print(header_dic)
        total_size = header_dic['total_size']

        # 第四步:接收真实的数据
        recv_size = 0
        recv_data = b''
        while recv_size < total_size:
            res = phone.recv(1024)  # 1024是一个坑,但这里已经解决啦
            recv_data += res
            recv_size += len(res)
        print(recv_data.decode('gbk'))  # 这里是gbk,因为是这里是在windows上运行的,windows的默认编码是gbk,其他操作系统的要注意

phone.close()
View Code


简单多次通信
server ,并且当客户端断开的时候,服务端自动等待下一次链接,而不是报错(windows下的情况,windows是try except 来捕获异常,linux下判断收到的是否为空即可,这里是windows的情况)(windows是报错,linux是死循环)。
# -*- coding: utf-8 -*-
import socket

'''
Socket套接字方法
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

family(socket家族)
socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

socket type类型
socket.SOCK_STREAM #for tcp
socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET #废弃了
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)

proto=0 请忽略,特殊用途
fileno=None 请忽略,特殊用途
'''
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 如果端口已经被使用,则回收再用
phone.bind(('localhost', 7001))  # 0-65535:0-1024给操作系统使用
phone.listen(5)
print('start')
while 1:
    conn, addr = phone.accept()
    print('有客户端连接上来了!')
    print(conn, addr)
    if_conn = 1
    while 1:
        try:
            data = conn.recv(1024)
            print(data.decode('utf-8'))
            conn.send(data.decode('utf-8').upper().encode('utf-8'))
        except ConnectionResetError:
            break
conn.close()
phone.close()
View Code

 client

# -*- coding: utf-8 -*-
import socket

'''
Socket套接字方法
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

family(socket家族)
socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

socket type类型
socket.SOCK_STREAM #for tcp
socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET #废弃了
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)

proto=0 请忽略,特殊用途
fileno=None 请忽略,特殊用途
'''
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('localhost', 7001))
while 1:
    cmd = input('>>:').strip()
    if cmd:
        phone.send(cmd.encode('utf-8'))
        get = phone.recv(1024)
        print(get.decode('utf-8'))

phone.close()
View Code


老男孩视频整理根据文件大小判断接收文件
#client同时进行了md5加密
import socket
import hashlib

client = socket.socket()
client.connect(('localhost',9998))
while True:
    cmd = input("请输入:").strip()
    if len(cmd)==0:continue
    if cmd.startswith('get'):
        client.send(cmd.encode())
        server_response = client.recv(1024)
        print("server response:",server_response)
        client.send(b'ready to recv file')
        file_total_size = int(server_response.decode())
        received_size = 0
        filename = cmd.split()[1]
        f = open(filename+'new','wb')
        m = hashlib.md5()

        while received_size < file_total_size:
            if file_total_size - received_size >1024:#收不止一次
                size = 1024
            else:#最后一次收了,剩多少收多少,不能多收
                size = file_total_size - received_size
                print('last receive:',size)

            data = client.recv(size)
            received_size += len(data)
            m.update(data)
            f.write(data)
        else:
            new_file_md5 = m.hexdigest()
            print('file receive done ',received_size,file_total_size)
            f.close()
        server_file_md5 = client.recv(1024)
        print('server file md5:',server_file_md5)
        print('client file md5',new_file_md5)

client.close()
View Code
#server
import socket,hashlib,os,time
server = socket.socket()
server.bind(('localhost',9998))
server.listen()


while True:
    conn,addr = server.accept()
    print('等待新指令:')
    data = conn.recv(1024)
    if not data:
        print('客户端已断开')
        break
    cmd,filename = data.decode().split()
    print(filename)
    if os.path.isfile(filename):

        f = open(filename,'rb')
        m = hashlib.md5()
        file_size = os.stat(filename).st_size
        conn.send(str(file_size).encode())
        conn.recv(1024)
        for line in f :
            m.update(line)
            conn.send(line)
        print('file md5',m.hexdigest())
        f.close()
        conn.send(m.hexdigest().encode()) #send md5
    print('send done')

server.close()
View Code
原文地址:https://www.cnblogs.com/Simonsun002/p/8168650.html