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实现数通设备端口监控示例
Apr 02 Python
Python常见数据结构详解
Jul 24 Python
进一步探究Python的装饰器的运用
May 05 Python
python虚拟环境的安装配置图文教程
Oct 20 Python
python之pandas用法大全
Mar 13 Python
python 实现判断ip连通性的方法总结
Apr 22 Python
Python简单定义与使用二叉树示例
May 11 Python
Python3.6简单反射操作示例
Jun 14 Python
Python数据抓取爬虫代理防封IP方法
Dec 23 Python
python实现控制台输出彩色字体
Apr 05 Python
python如何查看安装了的模块
Jun 23 Python
Python+uiautomator2实现自动刷抖音视频功能
Apr 29 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基础知识:类与对象(1)
2006/12/13 PHP
php随机输出名人名言的代码
2012/10/07 PHP
如何利用PHP执行.SQL文件
2013/07/05 PHP
CodeIgniter CLI模式简介
2014/06/17 PHP
php判断linux下程序问题实例
2015/07/09 PHP
php简单读取.vcf格式文件的方法示例
2017/09/02 PHP
PHP连接sftp并下载文件的方法教程
2018/08/26 PHP
判断脚本加载是否完成的方法
2009/05/26 Javascript
Jquery的Tabs内容轮换效果实现代码,几行搞定
2014/02/12 Javascript
nodejs获取本机内网和外网ip地址的实现代码
2014/06/01 NodeJs
javascript学习笔记整理(概述、变量、数据类型简介)
2015/10/25 Javascript
Sublime Text 3常用插件及安装方法
2015/12/16 Javascript
jQuery on()方法绑定动态元素的点击事件实例代码浅析
2016/06/16 Javascript
Bootstrap学习笔记 轮播(Carousel)插件
2017/03/21 Javascript
解决Mac node版本升级失败的问题
2018/05/16 Javascript
JS实现图片旋转动画效果封装与使用示例
2018/07/09 Javascript
layui radio性别单选框赋值方法
2018/08/15 Javascript
JS+HTML5 canvas绘制验证码示例
2018/12/05 Javascript
微信小程序实现多选删除列表数据功能示例
2019/01/15 Javascript
微信小程序如何获取群聊的openGid以及名称详解
2019/07/17 Javascript
Python常用内置函数总结
2015/02/08 Python
在Python中使用模块的教程
2015/04/27 Python
如何在Django中设置定时任务的方法示例
2019/01/18 Python
Python Selenium 之关闭窗口close与quit的方法
2019/02/13 Python
Python操作rabbitMQ的示例代码
2019/03/19 Python
pyqt5实现登录界面的模板
2020/05/30 Python
浅析PyTorch中nn.Linear的使用
2019/08/18 Python
瑞士国际航空官网:SWISS
2016/07/21 全球购物
GIVENCHY纪梵希官方旗舰店:高定彩妆与贵族护肤品
2018/04/16 全球购物
资料员的岗位职责
2013/11/20 职场文书
博士毕业生自我鉴定范文
2014/04/13 职场文书
小学评语大全
2014/04/22 职场文书
大班下学期个人总结
2015/02/13 职场文书
故意杀人案辩护词
2015/05/21 职场文书
小学体育跳绳课教学反思
2016/02/16 职场文书
全新239军机修复记
2022/04/05 无线电