【Python自动化运维之路Day9】Socket

socket也可以认为是套接字是一种源IP地址和目的IP地址以及源端口号和目的端口号的组合.网络化的应用程序在开始任何通讯之前都必须要创建套接字.就像电话的插口一样,没有它就没办法通讯.

socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

看图说话socket作用:

如下图是socket的工作流程:

根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。

(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。

(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

(3)连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

可以把socket连接过程想象成打电话,还是看实例更加形象,:

import socket
ip_port=(‘127.0.0.1‘,8888)
#买手机
s=socket.socket()

#插卡
s.bind(ip_port)
#开机
s.listen(5)   #最大连接5个

#等待电话
conn,addr=s.accept()
    #conn就是电话的链路
    #addr客户端的地址

#收消息
recv_data=conn.recv(1024)
print("客户端发送类型:",type(recv_data),recv_data)

#发消息
send_data=recv_data.upper()
conn.send(send_data)

#挂电话
conn.close()

socket_server

Socket server

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port=("127.0.0.1",8888)

#买手机
s=socket.socket()

#拨号
s.connect(ip_port)
#发送消息
send_data=input(">>>>:").strip()
s.send(bytes(send_data,encoding="utf-8"))

#收消息
recv_data=s.recv(1024)
print(str(recv_data,encoding="utf-8"))
#挂电话
s.close()

socket_client

Socket Client

sk.bind(address)

  s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。必须是,以元组(host,port)的形式表示地址。

sk.listen(backlog)

  开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

      backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
      这个值不能无限大,因为要在内核中维护连接队列

sk.setblocking(bool)

  是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

sk.accept()

  接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。

  接收TCP 客户的连接(阻塞式)等待连接的到来

sk.connect(address)

  连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

sk.connect_ex(address)

  同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

sk.close()

  关闭套接字

sk.recv(bufsize[,flag])

  接受的时候在python3中是字节的形式,so 打印的时候需要转换成字符str

  接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

sk.recvfrom(bufsize[.flag])

  与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.send(bytes[,flag])

  这个地方需要注意,在python3发送时必须是以字节的形式才能发送否则报错,python2中可以发送字符

  将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

sk.sendall(bytes[,flag])

  这个也是需要发送字节

  将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

      内部通过递归调用send,将所有内容发送出去。

sk.sendto(string[,flag],address)

  将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.settimeout(timeout)

  设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

sk.getpeername()

  返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

sk.getsockname()

  返回套接字自己的地址。通常是一个元组(ipaddr,port)

sk.fileno()

  套接字的文件描述符

Socket的具体参数

下面写了两个实例主要功能是能执行系统的一切命令并拿到返回值:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import subprocess #导入执行命令模块
ip_port=(‘127.0.0.1‘,9999) #定义元祖
#买手机
s=socket.socket()  #绑定协议,生成套接字
s.bind(ip_port)    #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式
s.listen(5)        #定义最大可以挂起胡链接数
#等待电话
while True:  #用来重复接收新的链接
    conn,addr=s.accept()   #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port
    #收消息
    while True: #用来基于一个链接重复收发消息
            try: #捕捉客户端异常关闭(ctrl+c)
                recv_data=conn.recv(1024) #收消息,阻塞
                if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出

                #发消息
                p=subprocess.Popen(str(recv_data,encoding=‘utf8‘),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平
                                                                                                      # 台命令的标准输出是gbk编码,需要转换
                res=p.stdout.read()   #获取标准输出
                if len(res) == 0:   #执行错误命令,标准输出为空,
                    send_data=‘cmd err‘
                else:
                    send_data=str(res,encoding=‘gbk‘)  #命令执行ok,字节gbk---->str---->字节utf-8

                send_data=bytes(send_data,encoding=‘utf8‘)

                #解决粘包问题
                ready_tag=‘Ready|%s‘ %len(send_data)
                conn.send(bytes(ready_tag,encoding=‘utf8‘)) #发送数据长度
                feedback=conn.recv(1024)  #接收确认信息
                feedback=str(feedback,encoding=‘utf8‘)

                if feedback.startswith(‘Start‘):
                    conn.send(send_data)  #发送命令的执行结果
            except Exception:
                break
    #挂电话
    conn.close()

socket_server1

Socket Server1

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port=(‘127.0.0.1‘,9999)
#买手机
s=socket.socket()
#拨号
s.connect(ip_port)  #链接服务端,如果服务已经存在一个好的连接,那么挂起

while True:        #基于connect建立的连接来循环发送消息
    send_data=input(">>: ").strip()
    if send_data == ‘exit‘:break
    if len(send_data) == 0:continue
    s.send(bytes(send_data,encoding=‘utf8‘))

    #解决粘包问题
    ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998
    ready_tag=str(ready_tag,encoding=‘utf8‘)
    if ready_tag.startswith(‘Ready‘):#Ready|9998
        msg_size=int(ready_tag.split(‘|‘)[-1])  #获取待接收数据长度
    start_tag=‘Start‘
    s.send(bytes(start_tag,encoding=‘utf8‘)) #发送确认信息

    #基于已经收到的待接收数据长度,循环接收数据
    recv_size=0
    recv_msg=b‘‘
    while recv_size < msg_size:
        recv_data=s.recv(1024)
        recv_msg+=recv_data
        recv_size+=len(recv_data)
        print(‘MSG SIZE %s RECE SIZE %s‘ %(msg_size,recv_size))

    print(str(recv_msg,encoding=‘utf8‘))
    #挂电话
s.close()

socket_client1

Socket Client1

上面说的socket的多线程:

socketserver模块

注意:在python3中都是小写了,在python2中写法是SocketServer

SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

1、ThreadingTCPServer基础

使用ThreadingTCPServer:

  • 创建一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Alex Li

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#import SocketServer
import socketserver,json
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        # print self.request,self.client_address,self.server
        self.request.sendall(bytes(‘欢迎致电 10086,请输入1xxx,0转人工服务.‘,encoding="utf-8"))
        while True:
            data = self.request.recv(1024)
            if len(data) == 0:break
            print("data", data)
            print("[%s] says:%s" % (self.client_address,data.decode() ))

            task_data = json.loads( data.decode()  )
            task_action = task_data.get("action")
            if hasattr(self, "task_%s"%task_action):
               func = getattr(self,"task_%s" %task_action)
               func(task_data)
            else:
               print("task action is not supported",task_action)

    def task_put(self,*args,**kwargs):
        print("---put",args,kwargs)
        filename = args[0].get(‘filename‘)
        filesize = args[0].get(‘file_size‘)
        server_response = {"status":200}
        self.request.send(bytes( json.dumps(server_response), encoding=‘utf-8‘  ))
        f = open(filename,‘wb‘)
        recv_size = 0
        while recv_size < filesize:
            data = self.request.recv(4096)
            f.write(data)
            recv_size += len(data)
            print(‘filesize: %s  recvsize:%s‘ % (filesize,recv_size))
        print("file recv success")
        f.close()

if __name__ == ‘__main__‘:
    server = socketserver.ThreadingTCPServer((‘0.0.0.0‘,8009),MyServer)
    server.serve_forever()

Socket Server2

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Alex Li

import socket
import os ,json
ip_port=(‘192.168.11.150‘,8009)
#买手机
s=socket.socket()
#拨号
s.connect(ip_port)
#发送消息
welcome_msg = s.recv(1024)
print("from server:",welcome_msg.decode())
while True:
    send_data=input(">>: ").strip()
    if len(send_data) == 0:continue

    cmd_list = send_data.split()
    if len(cmd_list) <2:continue
    task_type = cmd_list[0]
    if task_type == ‘put‘:
        abs_filepath = cmd_list[1]
        if os.path.isfile(abs_filepath):
            file_size = os.stat(abs_filepath).st_size
            filename = abs_filepath.split("\\")[-1]
            print(‘file:%s size:%s‘ %(abs_filepath,file_size))
            msg_data = {"action":"put",
                        "filename":filename,
                        "file_size":file_size}

            s.send(  bytes(json.dumps(msg_data),encoding="utf-8")  )
            server_confirmation_msg = s.recv(1024)
            confirm_data = json.loads(server_confirmation_msg.decode())
            if confirm_data[‘status‘] ==200:

                print("start sending file ",filename)
                f = open(abs_filepath,‘rb‘)
                for line in f:
                    s.send(line)

                print("send file done ")

        else:
            print("\033[31;1mfile [%s] is not exist\033[0m" % abs_filepath)
            continue
    else:
        print("doesn‘t support task type",task_type)
        continue
    #s.send(bytes(send_data,encoding=‘utf8‘))
    #收消息
    recv_data=s.recv(1024)
    print(str(recv_data,encoding=‘utf8‘))
    #挂电话
s.close()

Socket Client2

时间: 2024-08-04 07:50:57

【Python自动化运维之路Day9】Socket的相关文章

python自动化运维之路~DAY7

python自动化运维之路~DAY7 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.客户端/服务器架构 C/S 架构是一种典型的两层架构,其全称是Client/Server,即客户端服务器端架构,其客户端包含一个或多个在用户的电脑上运行的程序,而服务器端有两种,一种是数据库服务器端,客户端通过数据库连接访问服务器端的数据:另一种是Socket服务器端,服务器端的程序通过Socket与客户端的程序通信. C/S 架构也可以看做是胖客户端架构.因为客户端需要实现绝大多数的业务

python自动化运维之路~DAY10

python自动化运维之路~DAY10 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

【Python自动化运维之路Day7】基础篇

今日目录: 模块 configparser xml shutil zipfile tarfile subprocess 面向对象(上) 一. 模块 上一期博客里列出了几个常用模块(os,hashlib,sys,re), 还有几个剩余的,这篇来继续往下走. 1. configparser模块 configparser模块是Python自带模块,主要用于处理特定的文件(ini文件),格式比较像MySQL的配置文件类型,就是文件中有多个section,每个section下面有多个配置项,如下: [my

【Python自动化运维之路Day10】网络篇之网络socketserver

课堂笔记: 上周回顾: socket: 1. 导入模块 2. 创建socket 3. send sendall, sendall本质上也是调用send 但是在内部做了一个while循环,拿到消息,循环发,知道消息完全发送 sendall: while True: 10 = send('adkflskjf023jlskdf') 10 = send('adkflskjf023jlskdf') ... send,一次不一定能完全发送 10 = send('adkflskjf023jlskdf') 所以

《Python自动化运维之路》 系统基础信息模块(一)

系统性能收集模块Psutil 目录: 系统性能信息模块psutil 系统性能信息模块psutil psutil能够轻松实现获取系统运行的进程和系统利用率包括(CPU,内存,磁盘 和网络)等.主要用于系统监控.对于系统维护来说是个不错的模块. 1.模块的安装 wget https://files.pythonhosted.org/packages/14/a2/8ac7dda36eac03950ec2668ab1b466314403031c83a95c5efc81d2acf163/psutil-5.

python自动化运维之路4

装饰器 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 装饰器的应用场景:饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大

【Python自动化运维之路Day6】

1. configparser模块 import configparser config = configparser.ConfigParser() #先把config应用一下configparser,个人感觉有点像logging模块中的logger一样 config.read('db',encoding='utf-8') #读取配置文件,编码类型为utf-8 #使用sections()方法读取所有section,以列表形式返回 result = config.sections() print(

【Python自动化运维之路Day2】

1. 常量命名规则 在Python中,会在变量命名上标明某变量是常量,通常采用全是大写的方式来标明,如: CONNECT= '127.0.0.1' PORT = '3306' 2.Python编译 python先把源码文件(.py)编译成字节码文件(.pyc) python3执行后,生成了一个__pycache__目录,pyc会在此目录下,python2执行可以看到直接生成了一个.pyc文件 pyc  与py  时间戳不同,pyc才去重新编译 3  数据 <1> str 1.str1+str2

python自动化运维之路~DAY1

刚学python,有大神给指点指点的吗? #!/usr/bin/env python#_*_coding:utf8_*_import getpass,syscount = 0match_info = False #用布尔值来标志用户密码是否匹配,默认是flasewhile count < 3: username = input("\033[32;1mAsk you for a username :\033[0m") with open("locked.txt"