Python Socket编程详解


Posted in Python onApril 25, 2021

背景

关于Python Socket编程,首先需要了解几个计算机网络的知识,通过以下的几个问题,有助于更好的理解Socket编程的意义,以及整个框架方面的知识:

TCP和UDP协议本质上的区别?

TCP协议,面向连接,可靠,基于字节流的传输层通信协议;UDP协议无连接,不可靠,基于数据包的传输层协议。

TCP协议在建立连接的过程需要经历三次握手,断开连接则需要经历四次挥手,而这建立连接的过程增加了传输过程中的安全性。
而建立连接的过程则会消耗系统的资源,消耗更多的时间,而相比较UDP协议传输过程则不会出现这种问题。

总结来讲,基于TCP协议传输,需要不断的确认对方是否收到信息,从而建立连接(确认过程次数有限制,即三次握手),UDP协议传输则
不需要确认接收端是否收到信息,只需要将信息发给对方。

TCP/IP协议栈、HTTP协议、Socket之间的区别和联系?

TCP/IP协议栈就是一系列网络协议,可以分为四层模型来分析:应用层、传输层、网络层、链路层;

HTTP协议(超文本传输协议)就是在这一协议栈中的应用层协议;HTTP协议简单来说,它的作用就是规范数据的格式,让程序能够方便的识别,并且收发双方都需要遵循同样的协议格式进行数据传输。(应用层的协议也和HTTP协议的作用类似,不一样的是定义不同的数据格式。)

Socket可以理解为TCP/IP协议栈提供的对外的操作接口,即应用层通过网络协议进行通信的接口。Socket可以使用不同的网络协议进行端对端的通信;

TCP Socket服务器的通信过程?

Server端:

建立连接(socket()函数创建socket描述符、bind()函数绑定特定的监听地址(ip+port)、listen()函数监听socket、accept()阻塞等待客户端连接)

数据交互(read()函数阻塞等待客户端发送数据、write()函数发送给客户端数据)

Client端:

建立连接(socket()函数创建socket描述符、connect()函数向指定的监听地址发送连接请求)

数据交互(wirte()函数发送服务端数据、read()函数足阻塞等待接受服务端发送的数据)

socket和websocket之间的联系?

webosocket是一种通信协议,不同于HTTP请求,客户端请求服务端资源,服务端响应的通信过程;websocket允许服务端主动
向客户端推送消息,同时做到客户端和服务端双向通讯的协议。(具体底层原理有待后面实践,暂时未接触)

HTTP,WSGI协议的联系和区别?

HTTP协议(超文本传输协议),属于TCP/IP协议栈中应用层的协议。用于规范传输数据的格式,是一种客户端和服务端传输的规则。

WSGI协议则是Python定义的Web服务器和框架程序通信的接口规则。两者联系不大,强行说的话,Python框架程序主要处理的是HTTP请求。

(后期可以实现一个WSGI协议的Python框架,用于处理HTTP请求的实验。)

主流Web框架,异步Web框架?

主流Web框架:Django、Flask

异步Web框架:Tornado(内置异步模块)、Snaic(Python自带asyncio)、FastAPI(基于Starlette库) 、aiohttp(基于asyncio)

asyncio,aiohttp之间的联系?(异步编程)

asyncio是一个异步IO库,aiohttp就是基于asyncio的异步HTTP框架(支持客户端/服务端)

代码设计

Python提供了基本的socket模块:

  1. socket模块;提供了标准的BSD Sockets API;
  2. socketserver模块:提供了服务器中心类,简化服务器的开发;

TCP Socket服务端

socket模块:

# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM

def echo_handler(sock ,address):
	print("Get Connection from address:", address)

	while True:
		response = sock.recv(8192)
		if not response:
			break
		print(f"Got {response}")
		sock.sendall(response)

def echo_server(address, back_log=5):
	sock = socket(AF_INET, SOCK_STREAM)
	sock.bind(address)
	sock.listen(back_log)

	while True:
		sock_client, address = sock.accept()
		echo_handler(sock_client, address)

if __name__ == "__main__":
	echo_server(('localhost', 5000))

代码详解:

  • 创建一个基于IPV4和TCP协议的Socket,这里AF_INET指的是使用IPV4协议,SOCK_STREAM指定使用面向流的TCP协议,绑定监听端口,设置等待连接的最大数量
  • 创建一个永久循环,获取客户端请求的连接,accept()会等待并返回一个客户端的连接;
  • 连接建立后,等待客户端数据,接受完客户端数据,然后返回数据给客户端,最后关闭连接

存在的问题:当出现多个客户端请求时,由于是单个线程会发生阻塞的情况,所以如果需要多线程处理多个客户端请求,可以这样改;

from threading import Thread

while True:
        client_sock, address = sock.accept()
        thread = Thread(target=echo_handler, args=(client_sock, address))
        thread.start()

这样的话,就会在每个客户端请求的时候,生成一个子线程然后处理请求;
(但是存在一个问题:当突然大量请求连接,消耗系统资源达到上限后,很可能造成程序无法处理后续请求。)

socketserver模块:

from socketserver import BaseRequestHandler, TCPServer

class EchoHandler(BaseRequestHandler):
    def handle(self):
        print("Got Connection From: %s" % str(self.client_address))
        while True:
            msg = self.request.recv(8192)
            if not msg:
                break
            self.request.send(msg)

if __name__ == "__main__":
    server = TCPServer(("", 5000), EchoHandler)
    server.serve_forever()
from socketserver import StreamRequestHandler, TCPServer, ThreadingTCPServer
import time

class EchoHandler(StreamRequestHandler):
    def handle(self):
        print("Got Connection Address: %s" % str(self.client_address))
        for line in self.rfile:
            print(line)
            self.wfile.write(bytes("hello {}".format(line.decode('utf-8')).encode('utf-8')))

if __name__ == "__main__":
    serv = ThreadingTCPServer(("", 5000), EchoHandler)
    serv.serve_forever()

代码详解:

  • 处理多个客户端,初始化一个ThreadingTCPServer实例;
  • 设置绑定的IP地址和端口,以及处理类;
  • 使用StreamRequestHandler(使用流的请求处理程序类,类似file-like对象,提供标准文件接口简化通信过程),重写里面的handle方法,获取请求数据,返回数据给客户端;

TCP Socket客户端

socket模块:

# -*- coding: utf-8 -*-
from socket import socket, AF_INET, SOCK_STREAM
import time

def request_handler():
	start_time = time.time()
	sock_client = socket(AF_INET, SOCK_STREAM)
	sock_client.connect(('localhost', 5000))
	
	book_content = ""
	with open("send_books.txt", "r") as f:
		book_content = f.read()
	
	content_list = book_content.split("\n")
	for content in content_list:
		if content:
			sock_client.send((content).encode())
			time.sleep(2)
			response = sock_client.recv(8192)
			print(response)

	end_time = time.time()
	print("总共耗时:", end_time-start_time)

		

if __name__ == "__main__":
	request_handler()

UDP Socket

Socket模块:

from socket import socket, AF_INET, SOCK_DGRAM
import time

def time_server(address):
    sock = socket(AF_INET, SOCK_DGRAM)
    sock.bind(address)

    while True:
        msg, addr = sock.recvfrom(8192)
        print('Get message from', addr)
        resp = time.ctime()
        sock.sendto(resp.encode('ascii'), addr)

if __name__ == "__main__":
    time_server(('', 5000))

代码不详解,和之前的差不多,注意不同的协议就完事了

客户端测试:

from socket import socket, AF_INET, SOCK_DGRAM

if __name__ == "__main__":
    s = socket(AF_INET, SOCK_DGRAM)
    s.sendto(b'hello', ('localhost', 5000))
    text = s.recvfrom(8192)
    print(text)

socketserver模块:

from socketserver import BaseRequestHandler, UDPServer
import time


class TimeHandler(BaseRequestHandler):
    def handle(self):
        print("Got Connection %s".format(str(self.client_address)))
        data = self.request[0]
        print(data)
        msg, sock = self.request
        print(msg)
        data = time.ctime()
        sock.sendto(data.encode('ascii'), self.client_address)

if __name__ == "__main__":
    u = UDPServer(("localhost", 9999), TimeHandler)
    u.serve_forever()

代码不在赘述,如果需要多线程处理并发操作可以使用ThreadingUDPServer

总结

关于本篇介绍Python Socket编程,大都是皮毛,只是谈到了Python实际处理socket的几个模块,
关于socket底层方面的知识并未提及,先了解个大概,从实际使用方面出发,在实际使用过程中结合
计算机网络知识,能够对socket在整个TCP/IP协议栈中的作用。

socket和socketserver模块都可以用来编写网络程序,不同的是socketserver省事很多,你可以专注
业务逻辑,不用去理会socket的各种细节,包括不限于多线程/多进程,接收数据,发送数据,通信过程。

以上就是Python Socket编程详解的详细内容,更多关于Python Socket编程的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python通过floor函数舍弃小数位的方法
Mar 17 Python
Python Sqlite3以字典形式返回查询结果的实现方法
Oct 03 Python
windows下安装Python和pip终极图文教程
Mar 05 Python
定制FileField中的上传文件名称实例
Aug 23 Python
python利用高阶函数实现剪枝函数
Mar 20 Python
django 发送手机验证码的示例代码
Apr 25 Python
TensorFlow实现非线性支持向量机的实现方法
Apr 28 Python
Python基于最小二乘法实现曲线拟合示例
Jun 14 Python
Python采集猫眼两万条数据 对《无名之辈》影评进行分析
Dec 05 Python
Python爬虫爬取博客实现可视化过程解析
Jun 29 Python
小结Python的反射机制
Sep 28 Python
python图片灰度化处理的几种方法
Jun 23 Python
Python Django 后台管理之后台模型属性详解
如何用python反转图片,视频
python基于tkinter制作m3u8视频下载工具
用python自动生成日历
解决Django transaction进行事务管理踩过的坑
Apr 24 #Python
pdf论文中python画的图Type 3 fonts字体不兼容的解决方案
Apr 24 #Python
Python使用UDP实现720p视频传输的操作
You might like
PHP IN_ARRAY 函数使用注意事项
2010/07/24 PHP
php实现通用的从数据库表读取数据到数组的函数实例
2015/03/21 PHP
php实现QQ空间获取当前用户的用户名并生成图片
2015/07/25 PHP
无闪烁更新网页内容JS实现
2013/12/19 Javascript
jQuery的live()方法对hover事件的处理示例
2014/02/27 Javascript
JavaScript中对循环语句的优化技巧深入探讨
2014/06/06 Javascript
jQuery中prevUntil()方法用法实例
2015/01/08 Javascript
JavaScript中三种异步上传文件方式
2016/03/06 Javascript
js实现弹窗居中的简单实例
2016/10/09 Javascript
NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】
2017/09/06 NodeJs
vue无限轮播插件代码实例
2019/05/10 Javascript
JavaScript setInterval()与setTimeout()计时器
2019/12/27 Javascript
JavaScript前端实现压缩图片功能
2020/03/06 Javascript
[51:17]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.22
2019/09/05 DOTA
Python程序设计入门(5)类的使用简介
2014/06/16 Python
Python遍历文件夹和读写文件的实现代码
2016/08/28 Python
详解Python装饰器由浅入深
2016/12/09 Python
Python3 socket同步通信简单示例
2017/06/07 Python
python记录程序运行时间的三种方法
2017/07/14 Python
对Python 文件夹遍历和文件查找的实例讲解
2018/04/26 Python
对python-3-print重定向输出的几种方法总结
2018/05/11 Python
python 统计一个列表当中的每一个元素出现了多少次的方法
2018/11/14 Python
关于HTML5语义标签的实践(blog页面)
2016/07/12 HTML / CSS
翻新二手苹果产品的网络领导者:Mac of all Trades
2017/12/19 全球购物
为什么UNION ALL比UNION快
2016/03/17 面试题
平面设计的岗位职责
2013/11/08 职场文书
家长会学生家长演讲稿
2013/12/29 职场文书
设计师个人求职信范文
2014/02/02 职场文书
机电一体化求职信
2014/03/10 职场文书
五年级学生评语
2014/04/22 职场文书
法人委托书的范本格式
2014/09/11 职场文书
铁路安全反思材料
2014/12/24 职场文书
锦旗赠语
2015/06/23 职场文书
幼儿园小朋友毕业感言
2015/07/30 职场文书
python 如何用map()函数创建多线程任务
2021/04/07 Python
zabbix如何添加监控主机和自定义监控项
2022/08/14 Servers