Frank的学习之路

Day_9_总结_socket网络编程

socket网络编程

socket网络编程

1.OSI七层协议

2.基于tcp协议的套接字通信

3.模拟ssh远程执行命令

4.tcp的粘包问题已经解决方案

5.基于udp协议套接字通信

6.socketserver

目标:开发一个C/S架构的软件

C/S:客户端-----server

B/S:浏览器-----server

server

1、位置必须固定/绑定一个固定地址

2、对外一直提供服务,稳定运行

3、支持并发(让对个客户端感觉是同时被服务着)

网络:

    物理连接介质+互联网协议)(互联网协议就是世界英语)

    ip+port可以标识世界范围内独一无二的应用软件(基于网络通信)

任何的数据报都应该分为报头和数据两部分,其中报头是用来描述数据的,报头的长度是固定的

一、基于tcp协议的套接字通信(简单版)

服务端:

import  socket

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8080)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

print(conn,client_addr)

#5.\发消息

data=conn.recv(1024)

print('收到消息',data)

conn.send(data.upper())

#6.挂电话

conn.close()

#7.关机

phone.close()

客户端:

import socket

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8080)) #ipport应该是服务端的ip和端口

#3.\发消息

phone.send('hello'.encode('utf-8')) #必须是bytes类型

data=phone.recv(1024)

print('服务端消息',data)

#4.关闭

phone.close()

二、基于tcp协议的套接字通信(通信循环)

服务端收发消息增加while True:

服务端:

import  socket

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8080)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

print(conn,client_addr)

#5.\发消息

while True:

    try:

        data=conn.recv(1024)

        if len(data)==0:break #针对linux或者Mac

        print('收到消息',data)

        conn.send(data.upper())

    except ConnectionResetError: #针对windows

        break

#6.挂电话

conn.close()

#7.关机

phone.close()

#windows电脑cmd命令查看监听端口

netstat -an|findstr 8080

客户端:

import socket

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8080)) #ipport应该是服务端的ip和端口

#3.\发消息

while True:

    msg=input('>>>>:').strip()

    phone.send(msg.encode('utf-8')) #必须是bytes类型

    data=phone.recv(1024)

    print('服务端消息',data)

#4.关闭

phone.close()

因为服务端半连接池请求数限制5(服务端:phone.listen(5)),客户端最多可以打开5个客户端,每个客户端和服务端单线链接,不可以同时链接

三、基于tcp协议的套接字通信(通信循环+连接循环)

                            服务端请求链接增加while True: #循环链接

                            服务端:

                            import  socket

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8080)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

while True: #循环链接

    conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

    print(conn,client_addr)

    #5.\发消息

    while True:

        try:

            data=conn.recv(1024)

            if len(data)==0:break #针对linux或者Mac

            print('收到消息',data)

            conn.send(data.upper())

        except ConnectionResetError: #针对windows

            break

    #6.挂电话

    conn.close()

#7.关机

phone.close()

客户端:

                   import socket

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8080)) #ipport应该是服务端的ip和端口

#3.\发消息

while True:

    msg=input('>>>>:').strip()

    phone.send(msg.encode('utf-8')) #必须是bytes类型

    data=phone.recv(1024)

    print('服务端消息',data)

#4.关闭

phone.close()

四、bug处理

客户端增加一行代码:if len(msg)==0:continue

服务端:

                            import  socket

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8080)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

while True: #循环链接

    conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

    print(conn,client_addr)

    #5.\发消息

    while True:

        try:

            data=conn.recv(1024)

            if len(data)==0:break #针对linux或者Mac

            print('收到消息',data)

            conn.send(data.upper())

        except ConnectionResetError: #针对windows

            break

    #6.挂电话

    conn.close()

#7.关机

phone.close()

客户端:

import socket

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8080)) #ipport应该是服务端的ip和端口

#3.\发消息

while True:

    msg=input('>>>>:').strip()

    if len(msg)==0:continue

    phone.send(msg.encode('utf-8')) #必须是bytes类型

    data=phone.recv(1024)

    print('服务端消息',data)

#4.关闭

phone.close()

五、模拟ssh远程执行命令

Subprocess模块使用

import  subprocess

obj=subprocess.Popen('dir',

                     shell=True,

                     stdout=subprocess.PIPE,

                     stderr=subprocess.PIPE

                     )

# print(obj)

stdout=obj.stdout.read()

print(stdout.decode('GBK'))

stdout=obj.stdout.read()

print('第二次读',stdout)

注:stdout只能取一次数

说明:服务端增加subprocess模块,命令需要cmd.decode('utf-8')

客户端发送命令需要cmd.encode('utf-8'),接收输出消息需要res.decode('gbk')

服务端:

import  socket

import  subprocess

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8080)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

while True: #循环链接

    conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

    print(conn,client_addr)

    #5.\发消息

    while True:

        try:

            cmd=conn.recv(1024)

            if len(cmd)==0:break #针对linux或者Mac

            obj = subprocess.Popen(cmd.decode('utf-8'),

                                   shell=True,

                                   stdout=subprocess.PIPE,

                                   stderr=subprocess.PIPE

                                   )

            stdout = obj.stdout.read()

            stderr = obj.stderr.read()

            print(len(stdout)+len(stderr))

            conn.send(stdout+stderr)

        except ConnectionResetError: #针对windows

            break

    #6.挂电话

    conn.close()

#7.关机

phone.close()

客户端:

import socket

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8080)) #ipport应该是服务端的ip和端口

#3.\发消息

while True:

    cmd=input('>>>>:').strip()

    if len(cmd)==0:continue

    phone.send(cmd.encode('utf-8')) #必须是bytes类型

    res=phone.recv(1024)

    print('服务端消息',res.decode('gbk'))

#4.关闭

phone.close()

六、粘包问题

客户端通过time.sleep延迟发送三条信息,服务端能够正常接收到三条信息

服务端

import socket

server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议=>SOCK_STREAM

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

server.listen(5)

conn,client_addr=server.accept()

res1=conn.recv(1024)

print('第一次收: ',res1)

res2=conn.recv(1024)

print('第二次收: ',res2)

res3=conn.recv(1024)

print('第三次收: ',res3)

客户端:

import socket

import time

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

client.connect(('127.0.0.1',8081))

#tcpnagle算法:将发送的时间间隔比较短并且数据量小的多个数据包合成一个包发送

client.send(b'hello')

time.sleep(0.1)

client.send(b'world')

time.sleep(0.1)

client.send(b'frank')

执行结果:


七、自定义报头解决粘包问题

         Struct模块:

         import struct

#可以将整型打包成固定长度的bytes类型,可以基于网络传输

res=struct.pack('q',123)

print(res,len(res))

res1=struct.unpack('q',res)

print(res1[0])

import json

header_dic={

    'head_title':123123,

    'MD5':'adfasdf2343',

    'file':'a.txt'

}

header_json=json.dumps(header_dic)

print(type(header_json))

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

print(type(header_bytes))

obj=struct.pack('i',len(header_bytes))

print(obj,len(obj))

说明:服务端,导入struc模块,制作一个报头,并发送;客户端,先收报头,并解出报头总长度,接收文件通while循环判断是否收干净,直到收干净结束

服务端:

import  socket

import  subprocess

import struct

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8081)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

while True: #循环链接

    conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

    print(conn,client_addr)

    #5.\发消息

    while True:

        try:

            cmd=conn.recv(1024)

            if len(cmd)==0:break #针对linux或者Mac

            obj = subprocess.Popen(cmd.decode('utf-8'),

                                   shell=True,

                                   stdout=subprocess.PIPE,

                                   stderr=subprocess.PIPE

                                   )

            stdout = obj.stdout.read()

            stderr = obj.stderr.read()

            #先制作一个固定长度的报头

            header=struct.pack('i',len(stdout)+len(stderr))

            #发送报头

            conn.send(header)

            print(len(stdout)+len(stderr))

            conn.send(stdout+stderr)

        except ConnectionResetError: #针对windows

            break

    #6.挂电话

    conn.close()

#7.关机

phone.close()

客户端:

import socket

import struct

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8081)) #ipport应该是服务端的ip和端口

#3.\发消息

while True:

    cmd=input('>>>>:').strip()

    if len(cmd)==0:continue

    phone.send(cmd.encode('utf-8')) #必须是bytes类型

    #先收报头,从报头里面解出报头信息(数据总长度)

    header=phone.recv(4)

    total_size=struct.unpack('i',header)[0]

    #收数据

    res=b''

    recv_size=0

    while recv_size < total_size:

        data=phone.recv(1024)

        res+=data

        recv_size+=len(data)

    print('服务端消息',res.decode('gbk'))

#4.关闭

phone.close()

八、自定义报头解决粘包问题(终极版)

         说明:服务端,先制作报头header_dic,把它转换json格式header_json,转换二进制header_bytes,然后发送报头struct.pack('i',len(header_bytes))长度,在发送报头header_bytes

,再发送数据;客户端,先4bytes,然后提取报头header_size长度,然后精准提取报头header_bytes,转换header_json,获取hearer_dic,进而活动total_size

服务端:

import  socket

import  subprocess

import json

import struct

#1.买手机

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp流式协议==SOCK_STREAM

#2.插入手机卡

phone.bind(('127.0.0.1',8082)) #ip应该是服务端这个软件运行的那台机器的ip地址,port(0-65535)

#3.开机

phone.listen(5)#半连接池:控制的是同一时刻的链接请求数

print('服务端启动...')

#4.等待电话连线请求

while True: #循环链接

    conn,client_addr=phone.accept()#(套接字对象,存放有客户端的ip和端口的元组)

    print(conn,client_addr)

    #5.\发消息

    while True:

        try:

            cmd=conn.recv(1024)

            if len(cmd)==0:break #针对linux或者Mac

            obj = subprocess.Popen(cmd.decode('utf-8'),

                                   shell=True,

                                   stdout=subprocess.PIPE,

                                   stderr=subprocess.PIPE

                                   )

            stdout = obj.stdout.read()

            stderr = obj.stderr.read()

            #先制作报头

            header_dic={

                'total_size':len(stdout)+len(stderr),

                'filename':'a.txt'

            }

            header_json=json.dumps(header_dic)

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

            #发送报头的长度(固定4个字节)

            conn.send(struct.pack('i',len(header_json)))

            #再发报头

            conn.send(header_bytes)

            #再发送真实数据

            conn.send(stdout)

            conn.send(stderr)

        except ConnectionResetError: #针对windows

            break

    #6.挂电话

    conn.close()

#7.关机

phone.close()

客户端:

import socket

import struct

import json

#1.买手机

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

#2.拨号

phone.connect(('127.0.0.1',8082)) #ipport应该是服务端的ip和端口

#3.\发消息

while True:

    cmd=input('>>>>:').strip()

    if len(cmd)==0:continue

    phone.send(cmd.encode('utf-8')) #必须是bytes类型

    #4bytes,然后提取报头长度

    header_size=struct.unpack('i',phone.recv(4))[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']

    #最后接收真实数据

    res=b''

    recv_size=0

    while recv_size < total_size:

        data=phone.recv(1024)

        res+=data

        recv_size+=len(data)

    print('服务端消息',res.decode('gbk'))

#4.关闭

phone.close()

九、stockserver模块实现并发

服务端:

import socketserver

class MyTCPhandler(socketserver.BaseRequestHandler):

    def handle(self):

        while True:

            try:

                data=self.request.recv(1024)

                if len(data)==0:break

                print('收到%s:%s的数据:%s' % (self.client_address[0],

                                                self.client_address[1],

                                                data

                                        ))

                self.request.send(data.upper())

            except ConnectionResetError:

                break

if __name__ == '__main__':

    server=socketserver.ThreadingTCPServer(('127.0.0.1',9997),MyTCPhandler)

    socketserver.TCPServer.request_queue_size=10

    server.serve_forever()

         客户端:

         import socket

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

client.connect(('127.0.0.1',9997))

while True:

    msg=input('>>>:').strip()

    if len(msg)==0:break

    client.send(msg.encode('utf-8'))

    data=client.recv(1024)

    print('服务消息',data)

client.close()

返回顶部