第十七章 Python网络编程

简介:
Socket简介
在网络上的两个程序通过一个双向的通信连接实现数据的交换,这个链接的一端称为一个Socket(套接字),用于描述IP地址和端口。
建立网络通信连接至少要一对端口号(Socket),Socket本质是编程接口(API),对TCP/IP的封装,提供了网络通信能力。
每种服务都打开一个Socket,并绑定到端口上,不同的端口对应不同的服务,就像http对应80端口。
Socket是面向C/S(客户端/服务器)模型设计,客户端在本地随机申请一个唯一的Socket号,服务器拥有公开的socket,任何客户端都可以向它发送连接请求和信息请求。
比如:用手机打电话给10086客服,你的手机号就是客户端,10086客服是服务端。必须在知道对方电话号码前提下才能与对方通讯。
Socket数据处理流程如图:

17.1 socket
在Python中提供此服务的模块是socket和SocketServer,下面是socket常用的类、方法:
方法 描述
socket.socket([family[, type[, proto]]]) socket初始化函数,(地址族,socket类型,协议编号)协议编号默认0
socket.AF_INET IPV4协议通信
socket.AF_INET6 IPV6协议通信
socket.SOCK_STREAM socket类型,TCP
socket.SOCK_DGRAM socket类型,UDP
socket.SOCK_RAW 原始socket,可以处理普通socker无法处理的报文,比如ICMP
socket.SOCK_RDM 更可靠的UDP类型,保证对方收到数据
socket.SOCK_SEQPACKET 可靠的连续数据包服务
socket.socket()对象有以下方法:
accept() 接受连接并返回(socket object, address info),address是客户端地址
bind(address) 绑定socket到本地地址,address是一个双元素元组(host,port)
listen(backlog) 开始接收连接,backlog是最大连接数,默认1
connect(address) 连接socket到远程地址
connect_ex(address) 连接socket到远程地址,成功返回0,错误返回error值
getpeername() 返回远程端地址(hostaddr, port)
gettimeout() 返回当前超时的值,单位秒,如果没有设置返回none
recv(buffersize[, flags]) 接收来自socket的数据,buffersize是接收数据量
send(data[, flags]) 发送数据到socket,返回值是发送的字节数
sendall(data[, flags]) 发送所有数据到socket,成功返回none,失败抛出异常
setblocking(flag) 设置socket为阻塞(flag是true)或非阻塞(flag是flase)
温习下TCP与UDP区别:
TCP和UDP是OSI七层模型中传输层提供的协议,提供可靠端到端的传输服务。
TCPTransmission Control Protocol,传输控制协议),面向连接协议,双方先建立可靠的连接,再发送数据。适用于可靠性要求高的应用场景。
UDPUser Data Protocol,用户数据报协议),面向非连接协议,不与对方建立连接,直接将数据包发送给对方,因此 相对TCP传输速度快 。适用于可靠性要求低的应用场景。
17.1.1 TCP编程
下面创建一个服务端TCP协议的Socket演示下。
先写一个服务端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket

HOST = ''                 # 为空代表所有可用的网卡
PORT = 50007              # 任意非特权端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)   # 最大连接数
conn, addr = s.accept()   # 返回客户端地址
print 'Connected by', addr
while 1:
    data = conn.recv(1024)   # 每次最大接收客户端发来数据1024字节
    if not data: break       # 当没有数据就退出死循环 
    print "Received: ", data # 打印接收的数据
    conn.sendall(data)       # 把接收的数据再发给客户端
conn.close()
再写一个客户端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket

HOST = '192.168.1.120'    # 远程主机IP
PORT = 50007              # 远程主机端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('Hello, world') # 发送数据
data = s.recv(1024)       # 接收服务端发来的数据
s.close()
print 'Received: ', data
写好后,打开一个终端窗口执行:
# python socket-server.py
监听中...
# 直到客户端运行会接收到下面数据并退出
Connected by ('192.168.1.120', 37548)
Received:  Hello, world
再打开一个终端窗口执行:
# 如果端口监听说明服务端运行正常
# netstat -antp |grep 50007
tcp        0      0 0.0.0.0:50007           0.0.0.0:*               LISTEN      72878/python
# python socket-client.py
Received: Hello, world
通过实验了解搭到Socket服务端工作有以下几个步骤:
1)打开socket
2)绑定到一个地址和端口
3)监听进来的连接
4)接受连接
5)处理数据
17.1.2 UDP编程
服务端:
import socket
HOST = ''               
PORT = 50007             
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((HOST, PORT))
while 1:
    data, addr = s.recvfrom(1024)
    print 'Connected by', addr
    print "Received: ", data
    s.sendto("Hello %s"% repr(addr), addr)
conn.close()
客户端:
import socket
HOST = '192.168.1.99'                 
PORT = 50007             
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(data, (HOST, PORT))
data = s.recv(1024)
s.close()
print 'Received: ', data
运行方式与TCP编程一样。
使用UDP协议时,服务端就少了listen()和accept(),不需要建立连接就直接接收客户端的数据,也是把数据直接发送给客户端。
客户端少了connect(),同样直接通过sendto()给服务器发数据。
而TCP协议则前提先建立三次握手。
17.1.3 举一个更直观的socket通信例子
客户端发送bash命令,服务端接收到并执行,把返回结果回应给客户端。
服务端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import subprocess
import socket

HOST = ''               
PORT = 50007             
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(1)
except socket.error as e:
    s.close()
    print e
    sys.exit(1)

while 1:
    conn, addr = s.accept()
    print 'Connected by', addr
    while 1:
        # 每次读取1024字节
        data = conn.recv(1024)
        if not data: # 客户端关闭服务端会收到一个空数据
            print repr(addr) + " close."
            conn.close()
            break     
        print "Received: ", data
        cmd = subprocess.Popen(data, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        result_tuple = cmd.communicate()
        if cmd.returncode != 0 or cmd.returncode == None:
            result = result_tuple[1]
            # result = cmd.stderr.read()
        else:
            result = result_tuple[0]
            # result = cmd.stdout.read()  # 读不到标准输出,不知道为啥,所以不用
        if result:
            conn.sendall(result)
        else:
            conn.sendall("return null")
s.close()
客户端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import socket

HOST = '192.168.1.120'   
PORT = 50007             
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
except socket.error as e:
    s.close()
    print e
    sys.exit(1)
while 1:
    cmd = raw_input("Please input command: ")
    if not cmd: continue
    s.sendall(cmd)
    recv_data = s.recv(1024)
    print 'Received: ', recv_data
s.close()
查看运行效果, 先运行服务端,再运行客户端:
# python socket-server.py
Connected by ('192.168.1.120', 45620)
Received:  ls
Received:  touch a.txt
Received:  ls

# python socket-client.py
Please input command: ls
Received: 
socket-client.py
socket-server.py

Please input command: touch a.txt
Received:  return null
Please input command: ls
Received: 
a.txt
socket-client.py
socket-server.py

Please input command:
我想通过上面这个例子你已经大致掌握了socket的通信过程。
再举一个例子,通过socket获取本机网卡IP:
>>> socket.gethostname()
'ubuntu'
>>> socket.gethostbyname(socket.gethostname())
'127.0.1.1'

>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.connect(('10.255.255.255', 0))
>>> s.getsockname()
('192.168.1.120', 35765)
>>> s.getsockname()[0]
'192.168.1.120'

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ群:323779636(Shell/Python运维开发群)


17.2 SocketServer
ScoketServer是Socket服务端库,比socket库更高级,实现了多线程和多线程,并发处理多个客户端请求。
下面是几个常用的类:
SocketServer.TCPServer(server_addressRequestHandlerClassbind_and_activate=True) 服务器类,TCP协议
SocketServer.UDPServer(server_addressRequestHandlerClassbind_and_activate=True) 服务器类,UDP协议
SocketServer.BaseServer(server_addressRequestHandlerClass) 这个是所有服务器对象的超类。它定义了接口,不提供大多数方法,在子类中进行。
SocketServer.BaseRequestHandler 这个是所有请求处理对象的超类。它定义了接口,一个具体的请求处理程序子类必须定义一个新的handle()方法。
SocketServer.StreamRequestHandler 流式socket,根据socket生成读写socket用的两个文件对象,调用rfile和wfile读写
SocketServer.DatagramRequestHandler 数据报socket,同样生成rfile和wfile,但UDP不直接关联socket。这里rfile是由UDP中读取的数据生成,wfile则是新建一个StringIO,用于写数据
SocketServer.ForkingMixIn/ThreadingMixIn 多进程(分叉)/多线程实现异步。混合类,这个类不会直接实例化。用于实现处理多连接
SocketServer .BaseServer()对象有以下方法:
fileno() 返回一个整数文件描述符上服务器监听的套接字
handle_request() 处理一个请求
serve_forever(poll_interval=0.5) 处理,直至有明确要求shutdown()的请求。轮训关机每poll_interval秒
shutdown() 告诉serve_forever()循环停止并等待
server_close() 清理服务器
address_family 地址族
server_address 监听的地址
RequestHandlerClass 用户提供的请求处理类
socket socket对象上的服务器将监听传入的请求
allow_reuse_address 服务器是否允许地址的重用。默认False
request_queue_size 请求队列的大小。
socket_type socket类型。socket.SOCK_STREAM或socket.SOCK_DGRAM
timeout 超时时间,以秒为单位
finish_request() 实际处理通过实例请求RequestHandleClass并调用其handle()方法
get_request() 必须接受从socket的请求,并返回
handle_error(request, client_address) 如果这个函数被条用handle()
process_request(request, client_address)
server_activate()
server_bind() 由服务器构造函数调用的套接字绑定到所需的地址
verify_request(request, client_address) 返回一个布尔值,如果该值是True,则该请求将被处理,如果是False,该请求将被拒绝。
创建一个服务器需要几个步骤:
1)创建类,继承请求处理类(BaseRequestHandler),并重载其handle()方法,此方法将处理传入的请求
2)实例化服务器类之一,它传递服务器的地址和请求处理程序类
3)调用handle_request()或serve_forever()服务器对象的方法来处理一个或多个请求
4)调用server_close()关闭套接字
17.2.1 TCP编程
服务端:
#!/usr/bin/python
# -*- coding: utf-8 -*
import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    """
    请求处理程序类。
    每个连接到服务器都要实例化一次,而且必须覆盖handle()方法来实现与客户端通信
    """
    def handle(self):
        # self.request 接收客户端数据
        self.data = self.request.recv(1024).strip()
        print "%s wrote:" % (self.client_address[0])
        print self.data
        # 把接收的数据转为大写发给客户端
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    # 创建服务器并绑定本地地址和端口
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    # 激活服务器,会一直运行,直到Ctrl-C中断
    server.serve_forever()
另一个请求处理程序类,利用流(类文件对象简化通信提供标准文件接口):
class MyTCPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        # self.rfile创建的是一个类文件对象处理程序,就可以调用readline()而不是recv()
        self.data = self.rfile.readline().strip()
        print "%s wrote:" % (self.client_address[0])
        print self.data
        # 同样,self.wfile是一个类文件对象,用于回复客户端
        self.wfile.write(self.data.upper())
客户端:
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

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

try:
    sock.connect((HOST, PORT))
    sock.sendall(data + "\n")

    received = sock.recv(1024)
finally:
    sock.close()

print "Sent: %s" % data
print "Received: %s" % received
服务端结果:
# python TCPServer.py
127.0.0.1 wrote:
hello
127.0.0.1 wrote:
nice
客户端结果:
# python TCPClient.py hello
Sent: hello
Received: HELLO
# python TCPClient.py nice
Sent: nice
Received: NICE
17.2.2 UDP编程
服务端:
import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request[0].strip()
        self.socket = self.request[1]
        print "%s wrote:" % (self.client_address[0])
        print self.data
        self.socket.sendto(self.data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.UDPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()
客户端:
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

sock.sendto(data + "\n", (HOST, PORT))
received = sock.recv(1024)

print "Sent: %s" % data
print "Received: %s" % received
与TCP执行结果一样。
17.2.3 异步混合
创建异步处理,使用ThreadingMixIn和ForkingMixIn类。
ThreadingMixIn类的一个例子:
#!/usr/bin/python
# -*- coding: utf-8 -*
import socket
import threading
import SocketServer

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        data = self.request.recv(1024)
        cur_thread = threading.current_thread()
        response = "%s: %s" % (cur_thread.name, data)
        self.request.sendall(response)

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

def client(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    try:
        sock.sendall(message)
        response = sock.recv(1024)
        print "Received: %s" % response
    finally:
        sock.close()

if __name__ == "__main__":
    # 端口0意味着随机使用一个未使用的端口
    HOST, PORT = "localhost", 0

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address

    # 服务器启动一个线程,该线程将开始。每个线程处理每个请求
    server_thread = threading.Thread(target=server.serve_forever)
    # 作为守护线程
    server_thread.daemon = True
    server_thread.start()
    print "Server loop running in thread:", server_thread.name

    client(ip, port, "Hello World 1")
    client(ip, port, "Hello World 2")
    client(ip, port, "Hello World 3")

    server.shutdown()
    server.server_close()

# python socket-server.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

目录
相关文章
|
4月前
|
机器学习/深度学习 人工智能 算法
猫狗宠物识别系统Python+TensorFlow+人工智能+深度学习+卷积网络算法
宠物识别系统使用Python和TensorFlow搭建卷积神经网络,基于37种常见猫狗数据集训练高精度模型,并保存为h5格式。通过Django框架搭建Web平台,用户上传宠物图片即可识别其名称,提供便捷的宠物识别服务。
524 55
|
3月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
眼疾识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了4种常见的眼疾图像数据集(白内障、糖尿病性视网膜病变、青光眼和正常眼睛) 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Django框架搭建了一个Web网页平台可视化操作界面,实现用户上传一张眼疾图片识别其名称。
291 5
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
|
5天前
|
存储 监控 算法
基于 Python 哈希表算法的局域网网络监控工具:实现高效数据管理的核心技术
在当下数字化办公的环境中,局域网网络监控工具已成为保障企业网络安全、确保其高效运行的核心手段。此类工具通过对网络数据的收集、分析与管理,赋予企业实时洞察网络活动的能力。而在其运行机制背后,数据结构与算法发挥着关键作用。本文聚焦于 PHP 语言中的哈希表算法,深入探究其在局域网网络监控工具中的应用方式及所具备的优势。
36 7
|
13天前
|
存储 数据库 Python
利用Python获取网络数据的技巧
抓起你的Python魔杖,我们一起进入了网络之海,捕捉那些悠游在网络中的数据鱼,想一想不同的网络资源,是不是都像数不尽的海洋生物,我们要做的,就是像一个优秀的渔民一样,找到他们,把它们捕获,然后用他们制作出种种美味。 **1. 打开魔法之门:请求包** 要抓鱼,首先需要一个鱼网。在Python的世界里,我们就是通过所谓的“请求包”来发送“抓鱼”的请求。requests是Python中常用的发送HTTP请求的库,用它可以方便地与网络上的资源进行交互。所谓的GET,POST,DELETE,还有PUT,这些听起来像偶像歌曲一样的单词,其实就是我们鱼网的不同方式。 简单用法如下: ``` im
46 14
|
1月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【害虫识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
害虫识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了12种常见的害虫种类数据集【"蚂蚁(ants)", "蜜蜂(bees)", "甲虫(beetle)", "毛虫(catterpillar)", "蚯蚓(earthworms)", "蜚蠊(earwig)", "蚱蜢(grasshopper)", "飞蛾(moth)", "鼻涕虫(slug)", "蜗牛(snail)", "黄蜂(wasp)", "象鼻虫(weevil)"】 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Djan
128 1
基于Python深度学习的【害虫识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
|
5月前
|
Python
Python中的异步编程:使用asyncio和aiohttp实现高效网络请求
【10月更文挑战第34天】在Python的世界里,异步编程是提高效率的利器。本文将带你了解如何使用asyncio和aiohttp库来编写高效的网络请求代码。我们将通过一个简单的示例来展示如何利用这些工具来并发地处理多个网络请求,从而提高程序的整体性能。准备好让你的Python代码飞起来吧!
235 2
|
8天前
|
数据采集 存储 监控
Python 原生爬虫教程:网络爬虫的基本概念和认知
网络爬虫是一种自动抓取互联网信息的程序,广泛应用于搜索引擎、数据采集、新闻聚合和价格监控等领域。其工作流程包括 URL 调度、HTTP 请求、页面下载、解析、数据存储及新 URL 发现。Python 因其丰富的库(如 requests、BeautifulSoup、Scrapy)和简洁语法成为爬虫开发的首选语言。然而,在使用爬虫时需注意法律与道德问题,例如遵守 robots.txt 规则、控制请求频率以及合法使用数据,以确保爬虫技术健康有序发展。
|
2月前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【蘑菇识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
蘑菇识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了9种常见的蘑菇种类数据集【"香菇(Agaricus)", "毒鹅膏菌(Amanita)", "牛肝菌(Boletus)", "网状菌(Cortinarius)", "毒镰孢(Entoloma)", "湿孢菌(Hygrocybe)", "乳菇(Lactarius)", "红菇(Russula)", "松茸(Suillus)"】 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Django框架搭建了一个Web网页平台可视化操作界面,
159 11
基于Python深度学习的【蘑菇识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能
|
1月前
|
机器学习/深度学习 API Python
Python 高级编程与实战:深入理解网络编程与异步IO
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发和 API 设计。本文将深入探讨 Python 在网络编程和异步IO中的应用,并通过实战项目帮助你掌握这些技术。
|
3月前
|
安全 Linux 网络安全
利用Python脚本自动备份网络设备配置
通过本文的介绍,我们了解了如何利用Python脚本自动备份网络设备配置。该脚本使用 `paramiko`库通过SSH连接到设备,获取并保存配置文件。通过定时任务调度,可以实现定期自动备份,确保网络设备配置的安全和可用。希望这些内容能够帮助你在实际工作中实现网络设备的自动化备份。
111 14