快速了解Python socket编程

在 《MicroPython(ESP32/ESP8266) 实现web控制GPIO》 中有使用到 Python socket 来在开发板上实现一个简单的web服务,但在上文中并未对其进行详细的说明。本文通过结合 Python 官方文档,对 Python socket 编程进行一个梳理,同时加深理解。

socket

这里的 socket 通常指 Network socket,中文名叫网络套接字,主要用于主机间或进程间的数据通讯。相信很多第一次接触这个名词,都会一头雾水,因为这个套接字的翻译有即拗口也很难从字面上理解,而实际上,socket 这个单词,就是插座、接口、插孔的意思,我们可以简单的按这个单词的理解,socket 就是一个接口,当我们定义好插座的形式和内容,然后连接两边的插座,就可以通讯了。

简单的socket通讯实践

下面就开始来尝试用代码实现。既然要实现通讯,那么我们就需要一个服务端和一个客户端。我们需要分别创建两个文件 server.pyclient.py ,具体代码如下:

# server.py
import socket

HOST = ''
PORT = 5050

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(2)
    coon, addr = s.accept()
    with coon:
        print('Connected by', addr)
        while True:
            data = coon.recv(1024)
            if not data:
                break
            coon.sendall(data)
# client.py
import socket

HOST = 'localhost'
PORT = 5050

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello Server!')
    data = s.recv(1024)
print('Recevied', repr(data))

接下来,我们可以打开两个终端,分别运行 server.pyclient.py 。运行结果如下

# server
python server.py 
>
Connected by ('127.0.0.1', 51884)

# client
python client.py 
>
Recevied b'Hello Server!'

上面的代码实现了这样的功能:

服务端创建了一个 socket,然后绑定了端口和本地的ip,运行后就开始监听连接,当有客户端连接后打印出连接主机的ip和端口,并将客户端发送过来的内容发送回给客户端。而客户端的工作,就是创建一个 socket,并且连接服务器,给服务器发送一条消息并将服务器返回的内容打印出来。

根据上面的示例,我们可以归纳 socket 的最基本使用方法:

对于服务器:

  • 创建 socket

  • 绑定端口

  • 开启监听

  • 处理信息

对于客户端:

  • 创建 socket

  • 连接特定服务ip和端口

  • 处理信息

创建 socket,我们有几点需要主要,首先,socket 需要传入两个参数:协议簇(AddressFamily) 和 套接字类型(SocketKind) 。前者通常以 AF_ 开头,后者以 SOCK_ 开头。

我们常用的协议,主要有 IPv4 和 IPv6,对应是 socket.AF_INETsocket.AF_INET6 。而套接字类型,常用的是 socket.SOCK_STREAMsocket.SOCK_DGRAM 其中,SOCK_STREAM 通常用于 TCP,SOCK_DGRAM 用于 UDP。其中具体差异可以参考文末的参考资料。

实现代码也非常简单:

socket.socket(socket.AF_INET, socket.SOCK_STREAM)

接下来就到了绑定。绑定只需要在创建好的 socket 对象使用 bind() 函数即可,但这里有一点要注意,我们使用 AF_INET ,bind() 传入的参数需要写成 bind((host, port)),否则会报错 TypeError: socket.bind() takes exactly one argument (2 given)

然后就到了监听端口。这里服务端需要调用 listen()accept() 函数。其中 listen() 可以设置一个 timeout 参数,而 accept 返回一个 socket连接对象和客户端的ip端口信息。

接着我们来看客户端部分,客服端的部分就简单很多,创建 socket 和服务端一样,就不赘述了。客户端部分没有绑定和监听的步骤,我们只需要调用 connetc() 函数连接到服务器即可。

剩下的就是服务器端和客户端同样的信息处理部分。这部分也很简单,我们可以使用返回的连接socket对象 coon 来对信息进行发送接收操作。发送信息 coon.sendall() ,接收信息 coon.recv() 完成。其中,接收信息需要定义一个 bufsize,这个字节的长度可以自行根据实际情况定义。

到这里,最基础的 socket 编程介绍就已经完成。

简单 Web 页面服务

我们上面已经了解到了如何使用 socket 进行通讯。回到我们原本的话题,我们之前是在开发版上使用 socket 来构建一个 web 界面来方便我们控制开发版。那么就需要上上文的基础上再加以修改。

HTML 是我们常见网页的页面语言。要使用 HTTP 协议,我们在给客户端返回信息的时候,还需要加上 Headers 信息。我们可以用 Chrome 的开发者模式查看:

快速了解Python socket编程_第1张图片

上面的 Status Code 是状态码,200为返回成功,后面的 Content-Type 则是用于定义网络文件的类型,在代码中,我们需要手动添加:

conn.send(b'HTTP/1.1 200 OK\n')
conn.send(b'Content-Type: text/html\n')
conn.send(b'Connection: close\n\n')

这里需要注意,我们 send 的内容需要发送 bytes 类型的内容,如果内容是 str 会出现报错。

接下来,我们就可以把预先写好的 html 使用 conn.senall() 发送即可。

详细代码如下:

import socket

HOST = ''
PORT = 80

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)

# create html
def web_page():
    html = b"""
        
            Socket Test
            
            

This an socket test page.

"""
return html while True: conn, addr = s.accept() request = conn.recv(1024) print(str(request)) print('Connect: %s' % str(addr)) response = web_page() conn.send(b'HTTP/1.1 200 OK\n') conn.send(b'Content-Type: text/html\n') conn.send(b'Connection: close\n\n') conn.sendall(response) conn.close()

代码完成后,我们运行代码:

python server_web.py

然后再在浏览器中打开主机的ip地址,就可以看到页面了。

还有一点,就是关于参数的获取。我们的服务端代码中,有接收浏览器传来的信息 request = conn.recv(1024) ,我们把获取到的 request 打印出来,可以看到如下的信息:

b'GET / HTTP/1.1\r\nHost: 192.168.24.189\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 ...

这里是浏览器地址输入 192.168.24.189 返回的结果,如果我们想添加参数,就可以直接在我们的客户端浏览器地址了末尾加上参数 ?key=1 ,再看获取到的 request:

b'GET /?key=1 HTTP/1.1\r\nHost: 192.168.24.189\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 ...

这是,我们就可以看到,request 的开头从刚才的 / 变成了 /?key=1 ,这时,我们只需要处理这个参数值,即可实现我们的控制。具体使用案例可以参考《MicroPython(ESP32/ESP8266) 实现web控制GPIO》。

小结

本文先介绍了最基础的 socket 连接和传输用法,再通过一个简单的web服务来介绍如何构建一个 HTML 界面,最后还演示了如何利用利用 url 进行参数传递。希望本文对你有用。如果你觉得文章对你用,记得关注收藏。你的关注和收藏是继续更新的动力哦。

参考资料

  • 《socket — 底层网络接口》

  • 《What is SOCK_DGRAM and SOCK_STREAM?》

你可能感兴趣的:(Arduino,/,IoT,Coding,Note,python,socket,网络编程)