网络编程

本文为
痛点就是起点
原创文章,可以随意转载,但需注明出处。
Socket
提及网络编程,就不得不提 Socket(套接字) 概念。Socket 是网络编程中的一个抽象概念,通常我们用一个 Socket 表示“打开一个网络链接”,把打开链接的目标计算机的 IP 地址和端口号,以及协议类型等信息存储到 Socket 里。
python 进行网络编程时,实际上是 python 程序本身的进程连接到服务器进程的通信端口进行通信,可以看作是两个进程间的通信。python 提供了两个基本的 Socket 模块实现网络编程,分别为 Socket 和 SocketServer。
- Socket 提供了标准的 BSD Sockets API。
- SocketServer 提供了服务器中心类,可以简化网络服务器的开发。
Socket 类型
Socket 格式为:socket(family, type[,protocal]),需要给定地址族、套接字类型、协议编号来创建 Socket。
Socket类型 | 描述 |
---|---|
socket.AF_UNIX | 只能用于单一的 Unix 系统进程间通信 |
socket.AF_INET | 服务器之间网络通信 |
socket.AF_INET6 | 服务器之间 IPv6 网络通信 |
socket.SOCK_STREAM | 流式 socket,用于 TCP |
socket.SOCK_DGRAM | 数据报式 socket,用于 UDP |
socket.SOCK_RAM | 原始套接字,普通的套接字无法处理 ICMP、IGMP 等网络报文,而 SOCK_RAM 可以;其次,SOCK_RAM 也可以处理特殊的 IPv4 报文;此外,利用原始套接字,可以通过 IP_HDRINCL 套接字选项由用户构造 IP 头 |
socket.SOCK_SEQPACKET | 可靠的连续数据报服务 |
创建 TCP Socket | socket(socket.AF_INET, socket.SOCK_STREAM) |
创建 UDP Socket | socket(socket.AF_INET, socket.SOCK_DGRAM) |
Socket 函数
服务器 Socket 函数:
| Socket 函数 | 描述 | | —— | —— | | s.bind(address) | 将套接字绑定到地址,在 AF_INET 下,以元祖 (host, port) 的形式表示地址 | | s.listen(backlog) | 开始监听 TCP 传入连接。backlog 指定在拒绝连接之前,操作系统可以挂起的最大的连接数量。该值最小为1,大部分情况设为5就可以了 | | s.accept() | 接受 TCP 连接并返回 (conn, address) ,其中 conn 是新的套接字对象,可以用来接收和发送数据;address 是连接客户端的地址 | 客户端 Socket 函数:
Socket 函数 | 描述 |
---|---|
s.connect(address) | 连接到 address 处的套接字。一般 address 的格式为元祖 (host, port) ,如果连接出错,返回 socket.error 错误 |
s.connect_ex(address) | 功能与 connect(address) 相同,但是连接成功返回 0,连接失败返回 errno 的值 |
公共 Socket 函数:
Socket 函数 | 描述 |
---|---|
s.recv(bufsize[,flag]) | 接收 TCP 套接字数据,数据以字符串形式返回 |
s.send(string[,flag]) | 发送 TCP 套接字数据,返回发送数据的字节数 |
s.sendall(string[,flag]) | 完整发送 TCP 套接字数据,成功返回 None,失败抛出异常 |
s.recvfrom(bufsize[,flag]) | 接收 UDP 套接字数据,返回 (data, address) |
s.sendto(string[,flag],address) | 发送 UDP 套接字数据,返回发送数据的字节数 |
还有一些公共 Socket 函数,如:s.close(),s.getpeername(),s.getsockname(),s.setsockopt(level, optname, value),s.getsockopt(level, optname[,buflen]),s.settimeout(timeout),s.setblocking(flag) 等,这些函数一般可以见名知意,且使用频率较低,如果需要了解,都可以查询 API 获得。
TCP 编程
TCP 是一种面向连接的通信方式,连接可靠,通信双方以流的形式发送数据。主动发起连接的是客户端,被动响应的是服务端。服务端程序编写步骤如下。
- 创建 Socket,绑定 Socket 到本地 IP 与端口。
- 开始监听连接。
- 进入循环,不断连接客户端的连接请求。
- 接收传来的数据,并发送给对方数据(一般为确认收到)。
- 传输完毕后,关闭 Socket。
# coding:utf-8
import socket, threading, time
def dealClient(sock, addr):
# 第四步: 接收传来的数据,并发送给对方数据
print('Accept new connection from %s:%s' % addr)
sock.send(b'Hello,I am server!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
print('--- > %s !' % data.decode('utf-8'))
sock.send(('Loop_msg: %s!' % data.decode('utf-8')).encode('utf-8'))
# 第五步: 关闭 Socket
sock.close()
print('Connection from %s:%s' % addr)
if __name__ == '__main__':
# 第一步: 创建一个基于 IPV4 和 TCP 协议的 Socket
# Socket 绑定的 IP(127.0.0.1 为本机 IP)与端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999))
# 第二步: 监听链接
s.listen(5)
print('waiting for connection ...')
while True:
# 第三步: 接收一个新连接
sock, addr = s.accept()
t = threading.Thread(target=dealClient, args=(sock, addr))
t.start()
接下来,需要编写客户端程序,与服务端程序检修交互。客户端程序编写步骤如下。
- 创建 Socket,连接服务器地址。
- 连接后发送数据和接收数据。
- 传输完毕后,关闭 Socket。
# coding:utf-8
import socket
if __name__ == '__main__':
# 初始化 Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接目标的 IP 和端口
s.connect(('127.0.0.1', 9999))
# 接收消息
print('--- > ' + s.recv(1024).decode('utf-8'))
# 发送消息
s.send(b'Hello,I am client!')
print('--- > ' + s.recv(1024).decode('utf-8'))
s.send(b'exit')
# 关闭 Socket
s.close()
UDP 编程
UDP 是面向无连接的通信协议,使用 UDP 协议时,无需建立连接,只需知道对方的 IP 地址和端口号,就可以直接发送数据包,并不关心数据能否安全到达目的端。虽然 UDP 传输数据不可靠,没有建立连接的过程,但是 UDP 传输比 TCP 传输速度快,对于传输要求不严格的数据,可以采用 UDP 协议传输,从而提高速度。
UDP 服务端程序编写步骤:
- 创建 Socket,绑定制定的 IP 地址和端口。
- 直接发送数据和接收数据。
- 传输完毕后,关闭 Socket。
# coding:utf-8
import socket
if __name__ == '__main__':
# 创建 Socket ,绑定指定的 IP 和端口
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.2', 9999))
print('Bind UDP on 127.0.0.2:9999 ...')
while True:
# 直接发送数据和接收数据
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
UDP 客户端程序编写步骤:
- 创建 Socket。
- 直接可以与服务端程序进行数据交互。
- 传输完毕后,关闭 Socket。
# coding:utf-8
import socket
if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'hello', b'world']:
# 发送数据
s.sendto(data, ('127.0.0.2', 9999))
# 接收数据
print(s.recv(1024).decode('utf-8'))
s.close()