Python-100-Days之 网络编程socket对象 Day14plus++

Day14 plus++ 网络编程socket对象

socket:基于TCP/IP与应用层之间的抽象层,也就是接口。
分为服务端与客户端,双方之间建立通信循环。原理类似打电话。

–传输层协议
UDP套接字:基于端口,和TCP不同,没有双向请求连接,所以只是传输,不需要管对方有没有接收到。因此整体效率高。
TCP/IP:传输层协议,需要3次请求连接,又名流式协议。

–应用层协议:
HTTP/FTP 或者是自定义协议。两层协议之间用socket去解释,因为socket对TCP等协议进行了封装,避免直接去学习TCP/IP,过于底层,应该应用程序开发的效率。
详细解释:https://blog.csdn.net/g863402758/article/details/79359075

服务端

import socket
#买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #stream是tcp
print(phone)

#绑定IP端口,绑定手机卡
phone.bind(('127.0.0.1',8080))  #服务端软件IP地址
#0-65535,其中0-1024给操作系统使用

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

#等电话连接
print('starting........')
#res=phone.accept()
conn,client_addr=phone.accept()
print(client_addr)

#收发消息
while True:
    data=conn.recv(1024)      #调用操作系统,让操作系统接收数据并拷给服务端内存
    #接收数据最大数是byte类型,中文会编码 #空字符无效
    if not data:break           #data不为空才输出,防止死循环
    print('客户端数据:',data)

    conn.send(data.upper())  #发送大写数据

#挂电话
conn.close()

#关机
phone.close()

客户端

import socket
#买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print(phone)

#拨号
phone.connect(('127.0.0.1',8080))

#发 收信息
while True:#通信循环
    msg=input('>>: ').strip()
    if not msg:continue          #发送为空数据,就不继续;不为空一直发
    phone.send(msg.encode('utf-8')) #应用程序把自己内存的数据传给操作系统
    data=phone.recv(1024)           #应用程序让操作系统去调用数据
    #空字符输入无法输出,因为服务端接收空字符也无法收到
    print(data.decode('utf-8'))

#挂电话
phone.close()


另外:
会有出现多个客户端发送数据的情形,此时无法使用并发操作时,就需要对服务端进行连接循环的修改。也就是完成前一个请求accept并完成通信循环后关闭,进行下一个客户端的accept。

服务端修改

#当有多个客户端请求服务时,如何保证服务端的while循环一直调用,不用并发,只能排队了。
import socket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于网络类型的TCP协议
print(server) #检查一下
server.bind(('127.0.0.1',8080))  #绑定IP地址;0-65535,其中0-1024给操作系统使用
server.listen(4)          #最大挂起链接数,表示客户端最多挂4个
print('ready to start......')

while True:#连接循环
    conn,client_addr=server.accept()  #等客户端连接后建立连接
    print(client_addr)

    while True:#通信循环
        try:
            data=conn.recv(1024)
            print('接收客户端传来的数据',data)

            conn.send(data.upper())
        except ConnectionResetError:
            break
    conn.close()    #必须要等前一个完成通信循环关闭后才能accept后一个

phone.close()


#扩展:远程执行命令,得到结果并传递
----Windows下命令:

#dir:查看某一个文件夹下的子文件名与子文件夹名
#ipconfig:查看本地网卡IP信息
#tasklist:查看运行的进程

----Linux下命令:

#ls
#ifconfig
#ps aux

扩展后的服务端

----模拟ssh远程执行命令

import socket
import os
import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #stream是tcp
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
print(phone)

phone.bind(('127.0.0.1',8080))
phone.listen(5)

print('ready to start........')

while True:   ##链接循环
    conn,client_addr=phone.accept()
    print(client_addr)

    while True:  ##通信循环
        try:
            #1.收到命令
            cmd=conn.recv(1024)
            if not cmd:break
            print('客户端的数据',cmd)

            #2.执行命令拿到结果
            # res=os.system('ls /')   #res不能拿到os.system的运行结果,只能看到是否运行成功的标志
            # print('命令的结果是:',res) #error
            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            #字符串,命令解释器,把结果丢到原来管道,错误结果丢到另一个管道:启动一个程序去解析字符串,解析成命令执行
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            #看看输出结果是不是字节类型,是的话说明是要的结果//且只能取一次

            #3.命令的结果返回给客户端
            conn.send(stdout+stderr)  #+号可以优化
        except ConnectionResetError:
            break
    conn.close()

phone.close()

扩展后的客户端

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

while True:
    #1.发布命令
    cmd=input('>> ').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #2.接收数据
    data=phone.recv(1024)
    print(data.decode('utf-8'))

phone.close()

粘包现象

另外:注意到客户端接收数据,只能接收最多1024个字节的数据。在客户端发送数据,服务端接收并执行命令发送结果给客户端时,不知道结果字节数,如果超过了1024个字节,会堵在管道里。等到下一个命令发送,客户端收到的字节会继续接收上次没输出完成的字节。

----底层阐述

运行软件需要用到CPU,硬盘,内存
详细解释:https://www.cnblogs.com/zhangsanfeng/p/8891149.html

在这里插入图片描述

在客户端执行发送命令时,不是直接发送命令给服务端,而是通过调用操作系统去执行命令,客户端应用程序的内存与服务端应用程序的内存相互隔离。send只负责应用层,拷贝给操作系统内存;操作系统遵循传输层协议即TCP/IP协议。当把命令传输给服务端时,通过recv接收命令,recv可以分为2层。其一,数据到达操作系统内存;二,数据拷贝给应用程序。
不是一个send对应一个recv。

TCP中会运用的nagle算法:把时间间隔较短,数据量较小的数据打包发送,减少网络延迟,降低网络I/O带来的时间延迟。
socket层无法更改TCP协议中实现的算法,只能通过优化来解决。

如果recv一次接收的数据量小于传输过来的数据量,那么剩下的就会在第二次传输过来的时候接收,此时就会发生粘包。所以解决粘包最重要的做法是接收时知晓传输的数据本身大小。———计算出数据长度, 输出时用循环进行切割

此时需要引入struct模块。

举例如下:

import struct

res=struct.pack('i',1230)
"""
用法: pack(format, v1, v2, ...) -> bytes
返回一个字节对象,包含值V1,V2。format='i',表示转化为1个int,int类型占4个字节。
='ii',说明转化为2个int,占8个字节。
"""
print(res,len(res))


#client.recv(4)
obj=struct.unpack('i',res)
"""用法:转化为元组类型的数据"""
print(obj)

在服务端,命令结果返回给客户端时,传递报头,也就是数据长度给客户端。

----服务端

import socket
import struct
import os
import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #stream是tcp
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
print(phone)

phone.bind(('127.0.0.1',8081))
phone.listen(5)

print('ready to start........')

while True:   ##链接循环
    conn,client_addr=phone.accept()
    print(client_addr)

    while True:  ##通信循环
        try:
            #1.收到命令
            cmd=conn.recv(8096)
            if not cmd:break
            print('客户端的数据',cmd)

            #2.执行命令拿到结果
            # res=os.system('ls /')   #res不能拿到os.system的运行结果,只能看到是否运行成功的标志
            # print('命令的结果是:',res) #error
            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            #字符串,命令解释器,把结果丢到原来管道,错误结果丢到另一个管道:启动一个程序去解析字符串,解析成命令执行
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            #看看输出结果是不是字节类型,是的话说明是要的结果//且只能取一次


            #3.命令的结果返回给客户端
            #--第一步,把数据长度发送给客户端(也就是发报头,固定长度)
            print(len(stdout)+len(stderr)) #+号可以优化
            total_size=len(stdout)+len(stderr)
            header=struct.pack('i',total_size)  #只能传输字节类型,需要转

            #--第二步,把报头发送给客户端
            conn.send(header)

            #--第三步,再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError:
            break
    conn.close()

phone.close()

----客户端

import socket
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8081))

while True:
    #1.发布命令
    cmd=input('>> ').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #2.拿到命令结果,并打印
    #第一步,先拿到数据长度(收报头)
    header=phone.recv(4)
    #第二步,从报头中拿到关于命令结果的描述信息,得到传递的数据长度
    total_size=struct.unpack('i',header)[0]

    #第三部,接收真实的数据
    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('utf-8'))

phone.close()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值