Python 是一种简单的脚本语言,拥有强大的功能,笔者昨晚接触一个项目,用到 Socket 编程,之前就用过 Python 写过 Socket 项目,然记忆苦短,早已忘之。在网上搜到的各种信息良莠不济,并不能达到理想的效果。因而,在这里将 Python Socket 编程记录一下,方便网友参考,也方便自己日后用到此文。
socket_server = socket.socket(family, type)
这里的 socket() 函数的两个参数
socket_server.bind(address)
请注意,如果在创建 socket 对象时,family 类型选择的是 AF_INET,这里的 address 应当是一个元组类型的数据,即(ip地址,端口号)。
socket_server.listen(backlog)
backlog 通常是一个十进制的整型数,代表服务器允许连接的最大客户端数目。
con_socket, addr = socket_server.accept()
运行 accept 函数后,socket 进等待状态,等待客户请求连接。当客户请求连接时,accept 方法建立连接并返回服务器,返回一组含两个元素的元组(connection,address)。connection 是新的 socket 对象,服务器必须通过这个新的对象与客户端进行通信;address 是客户端的 Internet 地址。
通常使用一段循环,不断监听客户端发来的数据,或者向客户端发送数据
con.close()
socket_server.close()
先关闭 accept() 函数创建的 socket,再关闭服务端 socket
好了,说了这么多,言归正传,开始编写 TCP 服务端程序吧!
# -*- coding:utf-8 -*-
import socket
host = ("localhost", 2333)
socket_server = socket.socket()
socket_server.bind(host)
socket_server.listen(5)
while True:
socket_client, addr = socket_server.accept(socket.AF_INET, socket.SOCK_STREAM)
print("客户端:" + str(addr) + "已连接")
# 接收数据
receive_data = socket_client.recv(1024)
print(receive_data.decode("GB2312")) # 接收到的数据需要解码
# 发送数据
socket_client.sendall("哈哈哈,这是服务器发送的数据".encode("GB2312"))
socket_client.close()
socket_server.close()
如上,已经编写好 TCP 服务端,对于多数新手来说,不适宜编写客户端,再与服务器进行通信,这里安利一个小程序,即 网络调试助手 也叫 NetAssist。
这里需要注意的是,Python 发送的实际数据是字节序列,对于 Python3 来说,需要手动指定数据编码方式,由于这里使用了一个小程序作为客户端,而这个客户端使用的中文编码方式是 GB2312,所以服务端如果想发送中文数据,必须也按照此编码方式编码,不然客户端接收到的数据是乱码的。这其实也是将字节与字符串进行转换。
字符串与字节的转换方式还有其他形式,第三节有详细描述。
运行如上程序,再打开 NetAssist
服务端
至此,socket 服务端成功创建,模拟通信也 OK 了。
客户端的编写与服务器类似,不过呢,不需要监听端口,应为客户端是主动联系服务端的。
由于上面已经详细介绍一些函数的用法,这里简要说明一下客户端创建的过程。
首先是创建一个用于连接服务端的 socket
socket_client = socket.socket(family, type)
然后连接服务器,这里使用 connect() 函数,其实与服务器的 bind() 类似。需要注意的是,这里的 address 参数也是一个元组,仍然与 bind() 参数一样。
socket_client.connect(address)
连接成功之后,客户端就可以与服务端进行正常的通信了。仍然使用 send()/sendall() 和 recv() 进行消息的发送和接收。
最后需要关闭客户端的套接字
socket_client.close()
# -*- coding:utf-8 -*-
import socket
server = ("localhost", 2333)
socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_client.connect(server)
print("服务器:" + str(server) + " 已连接")
while True:
receive_data = socket_client.recv(1024)
print(receive_data.decode("GB2312"))
socket_client.sendall("这是一条来自客户端的消息".encode("GB2312"))
socket_client.close()
同样的,我们可以使用 NetAssist 进行调试,即使用该小程序创建一个客户端
客户端数据
最后呢,就可以将服务器和客户端一起连通起来,正式进行 socket 通信。
通过1,2 两节的说明,相信大家对 TCP 的服务端、客户端的创建都有了一个较为清晰的认识了。由于 UDP 是无连接的,因此比 TCP Socket 的创建更为简单。
与 TCP 明显不同的是
socket_udp = socket.socket(AF_INET, SOCK_DGRAM)
# -*- coding:utf-8 -*-
import socket
host = ("localhost", 2444)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#server_socket.bind(host)
while True:
#data, addr = server_socket.recvfrom(1024) # 与 TCP 不同之处
print("来自 " + str(addr) + " 的数据:" + str(data, encoding="GB2312")) # 字节转字符串
server_socket.sendto(bytes("服务器发送的数据", encoding="GB2312"), addr) # 字符串转字节
server_socket.close()
同样的,我们使用 NetAssist 辅助调试程序
编写的 UDP 服务端接受数据
其实可以看出,UDP 编程,服务器确实也可以当作客户端来使用,因为不涉及到 TCP 的三次握手,服务器和客户端都可以随意发送数据,而不需要等待对方回应。
这里需要说明一点的是,与 1.2节 在字符串与字节转换处理上不同的一点是:
这与下面(即1.2节)的转换方式其实是相同的