Python3 Socket编程

Python 是一种简单的脚本语言,拥有强大的功能,笔者昨晚接触一个项目,用到 Socket 编程,之前就用过 Python 写过 Socket 项目,然记忆苦短,早已忘之。在网上搜到的各种信息良莠不济,并不能达到理想的效果。因而,在这里将 Python Socket 编程记录一下,方便网友参考,也方便自己日后用到此文。

1 TCP Socket 服务器编写

1.1 基础知识

1.1.1 调用 socket.socket() 函数创建 Socket 对象

socket_server = socket.socket(family, type)

这里的 socket() 函数的两个参数

  • family 代表地址类型,主要有两个值:AF_INET,表示因特网的 IP 地址;AF_UNIX 代表同一台机器上进程间通信
  • type 代表套接字的类型,主要有 SOCK_STREAM(流套接字,TCP)以及 SOCK_DGRAM(数据报套接字,UDP)

1.1.2 使用 bind() 函数绑定IP地址及端口号

socket_server.bind(address)

请注意,如果在创建 socket 对象时,family 类型选择的是 AF_INET,这里的 address 应当是一个元组类型的数据,即(ip地址,端口号)。

1.1.3 使用 listen() 函数监听端口

socket_server.listen(backlog)

backlog 通常是一个十进制的整型数,代表服务器允许连接的最大客户端数目。

1.1.4 通过 accept() 函数等待客户请求一个连接

con_socket, addr = socket_server.accept()

运行 accept 函数后,socket 进等待状态,等待客户请求连接。当客户请求连接时,accept 方法建立连接并返回服务器,返回一组含两个元素的元组(connection,address)。connection 是新的 socket 对象,服务器必须通过这个新的对象与客户端进行通信;address 是客户端的 Internet 地址。

1.1.5 在循环体内接收或者发送数据

通常使用一段循环,不断监听客户端发来的数据,或者向客户端发送数据

  • send() 发送 二进制 消息,send() 返回值是:已发送的字符个数
  • recv() 接收客户端发来的消息,通常使用 recv(1024) 表示本次最大接受 1024 字节的数据,多余数据仍在留在缓冲区,下一次可继续接收

1.1.6 调用 socket.close() 关闭套接字

con.close()
socket_server.close()

先关闭 accept() 函数创建的 socket,再关闭服务端 socket

1.2 调试 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,所以服务端如果想发送中文数据,必须也按照此编码方式编码,不然客户端接收到的数据是乱码的。这其实也是将字节与字符串进行转换。

  • decoding() 将字符串使用相应编码方式转换成字节
  • encoding() 将字节按照某种编码方式转换成字符串

字符串与字节的转换方式还有其他形式,第三节有详细描述。
运行如上程序,再打开 NetAssist
Python3 Socket编程_第1张图片

服务端

至此,socket 服务端成功创建,模拟通信也 OK 了。

2 TCP Socket 客户端编写

客户端的编写与服务器类似,不过呢,不需要监听端口,应为客户端是主动联系服务端的。

2.1 基础介绍

由于上面已经详细介绍一些函数的用法,这里简要说明一下客户端创建的过程。
首先是创建一个用于连接服务端的 socket

socket_client = socket.socket(family, type)

然后连接服务器,这里使用 connect() 函数,其实与服务器的 bind() 类似。需要注意的是,这里的 address 参数也是一个元组,仍然与 bind() 参数一样。

socket_client.connect(address)

连接成功之后,客户端就可以与服务端进行正常的通信了。仍然使用 send()/sendall() 和 recv() 进行消息的发送和接收。

最后需要关闭客户端的套接字

socket_client.close()

2.2 客户端程序实战

# -*- 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 进行调试,即使用该小程序创建一个客户端
Python3 Socket编程_第2张图片
客户端数据
在这里插入图片描述
最后呢,就可以将服务器和客户端一起连通起来,正式进行 socket 通信。

3 UDP Socket 通信

通过1,2 两节的说明,相信大家对 TCP 的服务端、客户端的创建都有了一个较为清晰的认识了。由于 UDP 是无连接的,因此比 TCP Socket 的创建更为简单。

与 TCP 明显不同的是

  1. 在创建 socket 对象时,使用 SOCK_DGRAM
  2. UDP 编程严格意义上来说,不存在服务器和客户端的说法,同样一套程序,既可以作为服务端也可以作为客户端。只是通信的时候,通常会把原始发送方称之为 UDP 客户端,接收方称之为服务端
socket_udp = socket.socket(AF_INET, SOCK_DGRAM)

3.1 代码实例

# -*- 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 辅助调试程序
Python3 Socket编程_第3张图片
编写的 UDP 服务端接受数据
在这里插入图片描述
其实可以看出,UDP 编程,服务器确实也可以当作客户端来使用,因为不涉及到 TCP 的三次握手,服务器和客户端都可以随意发送数据,而不需要等待对方回应。

3.2 Python 中字节与字符串的转换

这里需要说明一点的是,与 1.2节 在字符串与字节转换处理上不同的一点是:

  • bytes(字符串, encoding=“GB2312”) 将字符串使用相应编码方式转换成字节
  • str(字节, decoding=“GB2312”) 将字节按照某种编码方式(这里是GB2312)转换成字符串

这与下面(即1.2节)的转换方式其实是相同的

  • 字符串.decoding(“GB2312”) 将字符串使用相应编码方式转换成字节
  • 字节.encoding(“GB2312”) 将字节按照某种编码方式转换成字符串

你可能感兴趣的:(数据结构与算法)