栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信python实例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信python实例

python 实现TCP socket通信和 HTTP服务器、服务器和客户端通信实例
      • socket是什么?
      • 服务器和客户端通信的流程
      • python 实现TCP socket通信例子
      • 关于Host和PORT的设置
      • socket函数
      • socket编程思路
      • 基于TCP socket的HTTP服务器
      • 分析HTTP服务器代码
        • 服务器的response文本
        • 客户端的request文本
      • 分析运行结果

socket是什么?

由下图可理解:Socket是应用层与TCP/IP协议族通信的中间软件抽象层。

复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

服务器和客户端通信的流程

由下图可理解服务器和客户端通信的流程:

服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。

在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。

客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

网络中进程之间通信:

首要解决的问题是如何唯一标识一个进程。

TCP/IP协议族已经帮我们解决了这个问题,

网络层的“ip地址”可以唯一标识网络中的主机,

而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。

这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

一个socket包含四个地址信息: 两台计算机的IP地址和两个进程所使用的端口(port)。IP地址用于定位计算机,而port用于定位进程。

python 实现TCP socket通信例子

在互联网上,我们可以让某台计算机作为服务器。

服务器开放自己的端口,被动等待其他计算机连接。

当其他计算机作为客户,主动使用socket连接到服务器的时候,服务器就开始为客户提供服务。

在Python中,我们使用标准库中的socket包来进行底层的socket编程。

服务器端:使用bind()方法来赋予socket以固定的地址和端口,并使用listen()方法来被动的监听该端口。当有客户尝试用connect()方法连接的时候,服务器使用accept()接受连接,从而建立一个连接的socket:

import socket

HOST = ''
PORT = 8000

reply = 'Yes'

'''
socket.socket()创建一个socket对象,
并说明socket使用的是IPv4(AF_INET,IP version 4)
和TCP协议(SOCK_STREAM)。
'''
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))

s.listen(3)

conn, addr = s.accept()

request = conn.recv(1024)

print('request is:', request.decode())
print('Connected by:', addr)

conn.sendall(reply.encode())

conn.close()


客户端:主动使用connect()方法来搜索服务器端的IP地址和端口,以便客户可以找到服务器,并建立连接:

import socket

HOST = '127.0.0.1'
PORT = 8000

request = 'can you hear me?'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

'''
TypeError: a bytes-like object is required, not 'str'
解决办法:
str→bytes:encode()方法。str通过encode()方法可以转换为bytes。
bytes→str: decode()方法。bytes通过decode()方法可以转换为str。
'''
s.sendall(request.encode())

reply = s.recv(1024)

# send 函数的参数和 recv 函数的返回值都是 bytes 类型

print('reply is:', reply.decode())
s.close()

服务器端运行结果:

request is: can you hear me?
Connected by: ('127.0.0.1', 1489)

客户端运行结果:

reply is: 'Yes'
关于Host和PORT的设置

我本机的ipv4地址是10.1.173.8。我把服务器和客户端都放到一个电脑上。

我测试了一下,在客户端代码中,我设置HOST = ‘10.1.173.8’。

然后,运行客户端和服务器代码,产生和HOST = '127.0.0.1’同样结果。

也就是说,客户端的HOST,为客户端IP想要connect的IP。

为了证明这一想法,我把客户端设置成HOST = ‘10.1.172.1’

再次运行,出现报错:

s.connect((HOST, PORT))
TimeoutError: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。

可见,如果没有两台计算机做实验,可以将客户端IP想要connect的IP改为"127.0.0.1",这是个特殊的IP地址,用来连接当地主机。

而且,如果知道服务器的IP,那么也就可以在客户端直接设置HOST = 服务器的ip

下面我测试一下端口号PORT:

服务器端和客户端 端口号都相同时候,可以建立连接。而且端口的数不会影响连接,都是8000和都是8080,都能连上。

端口号不同就连接不上。

socket函数
服务端socket函数描述
s.bind(address)将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址.
s.listen(backlog)开始监听TCP传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept()接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
客户端socket函数描述
s.connect()主动初始化TCP服务器连接。一般address的格式为元组(主机/ip,port),如果连接出错,返回socket.error错误。
s.connect_ex()connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共socket函数描述
s.recv()socket.recv(bufsize[, flags]),从套接字接收数据。返回值是表示接收到的数据的bytes对象。bufsize指定一次接收的最大数据量。一般为1024开始
s.send()发送数据,将数据发送到socket套接字。(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.close()关闭socket 套接字
socket编程思路

TCP服务端:

1 创建套接字,绑定套接字到本地IP与端口

# socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind()

2 开始监听连接 #s.listen()

3 进入循环,不断接受客户端的连接请求 #s.accept()

4 然后接收传来的数据,并发送给对方数据 #s.recv() , s.sendall()

5 传输完毕后,关闭套接字 #s.close()

TCP客户端:

1 创建套接字,连接远端地址

​ # socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.connect()

2 连接后发送数据和接收数据 # s.sendall(), s.recv()

3 传输完毕后,关闭套接字 #s.close()

基于TCP socket的HTTP服务器

上面的例子中,使用TCP socket来为两台远程计算机建立连接。

然而,socket传输自由度太高,从而带来很多安全和兼容的问题。

往往利用一些应用层的协议(比如HTTP协议)来规定socket使用规则,以及所传输信息的格式。

HTTP协议利用请求-回应(request-response)的方式来使用TCP socket。

客户端向服务器发一段文本作为request,服务器端在接收到request之后,向客户端发送一段文本作为response。

在完成了这样一次request-response交易之后,TCP socket被废弃。

下次的request将建立新的socket。

request和response本质上说是两个文本,只是HTTP协议对这两个文本都有一定的格式要求。

import socket

HOST = ''
PORT = 8000

text_content = b'''HTTP/1.x 200 OK  
Content-Type: text/html


WOW


Wow, Python Server

''' f = open('test.jpg', 'rb') pic_content = b''' HTTP/1.x 200 OK Content-Type: image/jpg ''' pic_content = pic_content+f.read() f.close() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) while True: s.listen(3) conn, addr = s.accept() request = conn.recv(1024) method = request.decode().split(' ')[0] src = request.decode().split(' ')[1] if method == 'GET': if src == '/test.jpg': content = pic_content else: content = text_content print('Connected by', addr) print('Request is:', request) conn.sendall(content) conn.close()

为了配合上面的服务器程序,在放置Python程序的文件夹里,保存了一个test.jpg图片文件。

在终端运行上面的Python程序,作为服务器端。

再打开一个浏览器作为客户端。

在浏览器的地址栏输入:127.0.0.1:8000,也可以用另一台电脑,并输入服务器的IP地址。

可以看到:

分析HTTP服务器代码 服务器的response文本

如我们上面所看到的,服务器会根据request向客户传输两条信息text_content和pic_content中的一条,作为response文本。

整个response分为**起始行(start line), 头信息(head)和主体(body)**三部分。

起始行就是第一行:

HTTP/1.x 200 OK

它实际上又由空格分为三个片段,HTTP/1.x表示所使用的HTTP版本,200表示状态(status code),200是HTTP协议规定的,表示服务器正常接收并处理请求,OK是供人来阅读的status code。

头信息跟随起始行,它和主体之间有一个空行。这里的text_content或者pic_content都只有一行的头信息,text_content的头信息

Content-Type: text/html

表示主体信息的类型为html文本

而pic_content的头信息

Content-Type: image/jpg

说明主体的类型为jpg图片(image/jpg)。

主体信息为html或者jpg文件的内容。

(注意,对于jpg文件,我们使用’rb’模式打开,是为了与windows兼容。因为在windows下,jpg被认为是二进制(binary)文件,在UNIX系统下,则不需要区分文本文件和二进制文件。)

客户端的request文本

用浏览器作为客户端。

request由客户端程序发给服务器。

尽管request也可以像response那样分为三部分,request的格式与response的格式并不相同。

request由客户发送给服务器,比如下面是一个request:

GET /test.jpg HTTP/1.x
Accept: text*;q=0.8,application/signed-exchange;v=b3;q=0.9rnSec-Fetch-Site: nonernSec-Fetch-Mode: navigaternSec-Fetch-User: ?1rnSec-Fetch-Dest: documentrnAccept-Encoding: gzip, deflate, brrnAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6rnrn'
    
Connected by ('127.0.0.1', 8177)
Request is: b'GET /test.jpg HTTP/1.1rnHost: 127.0.0.1:8000rnConnection: keep-alivernsec-ch-ua: "Chromium";v="94", "Microsoft Edge";v="94", ";Not A Brand";v="99"rnsec-ch-ua-mobile: ?0rnUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.50rnsec-ch-ua-platform: "Windows"rnAccept: image/webp,image/apng,image/svg+xml,image*;q=0.8rnSec-Fetch-Site: same-originrnSec-Fetch-Mode: no-corsrnSec-Fetch-Dest: imagernReferer: http://127.0.0.1:8000/rnAccept-Encoding: gzip, deflate, brrnAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6rnrn'
    
Connected by ('127.0.0.1', 6015)
Request is: b'GET /favicon.ico HTTP/1.1rnHost: 127.0.0.1:8000rnConnection: keep-alivernPragma: no-cachernCache-Control: no-cachernsec-ch-ua: "Chromium";v="94", "Microsoft Edge";v="94", ";Not A Brand";v="99"rnsec-ch-ua-mobile: ?0rnUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.50rnsec-ch-ua-platform: "Windows"rnAccept: image/webp,image/apng,image/svg+xml,image*;q=0.8rnSec-Fetch-Site: same-originrnSec-Fetch-Mode: no-corsrnSec-Fetch-Dest: imagernReferer: http://127.0.0.1:8000/rnAccept-Encoding: gzip, deflate, brrnAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6rnrn'

第三个请求 是因为浏览器默认都会去请求favicon.ico图标

favicon.ico 图标用于收藏夹图标和浏览器标签上的显示

上面的服务器程序中,用while循环来让服务器一直工作下去。实际上,还可以根据多线程的知识,将while循环中的内容改为多进程或者多线程工作。

服务器程序还不完善,还可以让Python程序调用Python的其他功能,以实现更复杂的功能。比如说制作一个时间服务器,让服务器向客户返回日期和时间。还可以使用Python自带的数据库,来实现一个完整的LAMP服务器。

socket包是比较底层的包。Python标准库中还有高层的包,比如SocketServer,SimpleHTTPServer,CGIHTTPServer,cgi。这些都包都是在帮助更容易的使用socket。如果已经了解了socket,那么这些包就很容易明白了。利用这些高层的包,可以写一个相当成熟的服务器。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/349584.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号