简介
网络编程就是在程序中实现网络中两台计算机的通信。而用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。
初识Socket
Socket(又称套接字)起源于Unix,是应用层与TCP/IP协议族通信的中间软件抽象层。复杂的TCP/IP协议族隐藏在了Socket接口内部,用户只需要简单地使用Socket接口来进行网络编程。应用程序通常是通过Socket向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
创建Socket
在Python中,我们用 socket()函数来创建套接字,语法格式如下:
-> socket(family,type[,protocol])
参数解释:
family:socket家族 | 描述 |
---|---|
socket.AF_UNIX | 只能够用于单一的Unix系统进程间通信 |
socket.AF_INET | 指定使用IPv4协议进行服务器间网络通信 |
socket.AF_INET6 | 指定使用IPv6协议进行服务器间网络通信 |
type:socket类型 | 描述 |
---|---|
socket.SOCK_STREAM | 流式socket , for TCP |
socket.SOCK_DGRAM | 数据报式socket , for UDP |
socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 |
socket.SOCK_SEQPACKET | 可靠的连续数据包服务 |
protocol:socket协议 | 描述 |
---|---|
一般不填且默认为0 | 系统会根据地址格式和套接字类别,自动选择一个合适的协议。 |
Socket常用方法
服务器端方法 | 描述 |
---|---|
socket.bind(address) | 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。 |
socket.listen(backlog) | 开始监听TCP传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
socket.accept() | 被动接受TCP客户端连接并返回(sock,address),其中sock是新的套接字对象,可以用来接收和发送数据,address是连接客户端的地址。 |
客户端方法 | 描述 |
---|---|
socket.connect(address) | 主动初始化TCP服务器连接,一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
socket.connect_ex(address) | connect()的扩展版本,出错时返回出错码,而不是抛出异常。 |
公共方法 | 描述 |
---|---|
socket.recv(bufsize[,flag]) | 接受TCP套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
socket.send(string[,flag]) | 发送TCP数据。将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 |
socket.sendall(string[,flag]) | 完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
socket.recvfrom(bufsize[.flag]) | 接受UDP套接字的数据。与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
socket.sendto(string[,flag],address) | 发送UDP数据。将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
socket.close() | 关闭套接字。 |
TCP编程
大多数网络通信连接都是可靠的TCP连接。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器;连接成功后,通信双方都能以流的形式发送数据。
在Python中用TCP协议进行Socket编程十分简单,对于客户端,要主动连接服务器的IP和指定端口,对于服务器,要首先监听指定端口,然后,对每一个新的连接,创建一个线程或进程来处理。通常,服务器程序会无限运行下去。要注意的是,一个端口不能同被两个Socket绑定。
下图展示了TCP服务端和客户端各自Socket创建以及它们之间的交互过程:
接下来是一个简单的示例程序(建议自己敲下来跑一遍),服务器端接收客户端的连接请求,把客户端发过来的字符串加上Hello再发回去。
服务器端代码:
#!/usr/bin/python
# 文件名:tcp_server.py
import socket
import threading
import time
# 实现连接成功后的交互,参数sock为套接字对象,addr为客户端地址
def tcplink(sock, addr):
# 输出连接成功的提示:
print('Accept new connection from %s:%s...' % addr)
# 发送TCP数据:
sock.send(b'Hello, What\'s your name?')
while True:
# 接收小于 1024 字节的数据
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'disconnect':
break
sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
# 关闭Socket,一次完整的网络通信就此结束
sock.close()
print('Connection from %s:%s closed.' % addr)
# 创建一个基于IPv4和TCP协议的socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址(host,port)到套接字:
s.bind(('127.0.0.1', 9999))
# 开始监听,最大连接数设为5:
s.listen(5)
print('TCP Server is running...')
print('Waiting for connection...')
while True:
# 被动接受TCP客户端连接,(阻塞式)等待连接的到来:
sock, addr = s.accept()
# 创建新线程来处理TCP连接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
客户端代码:
#!/usr/bin/python
# 文件名:tcp_client.py
import socket
# 创建一个基于IPv4和TCP协议的socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接,指定主机和端口:
s.connect(('127.0.0.1', 9999))
# 接收欢迎消息:
print(s.recv(1024).decode('utf-8'))
# 持续与服务器交互:
while True:
# 获取用户输入:
msg = input('Your input:')
if not msg or msg == 'quit':
break
# 发送数据:
s.send(msg.encode('utf-8'))
# 输出服务器返回的消息
print('From server:',s.recv(1024).decode('utf-8'))
# 发送断开连接的指令
s.send(b'disconnect')
# 套接字关闭
s.close()
打开三个命令行窗口,一个运行服务器端程序,另外两个运行客户端程序,效果如下图:
UDP编程
相对TCP,UDP则是面向无连接的协议。使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,数据包能否到达是无法确定的。
虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。
接下来我们使用UDP协议来实现与上面示例的程序。
服务器端代码:
#!/usr/bin/python
# 文件名:udp_server.py
import socket
# 创建一个基于IPv4和UDP协议的socket:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址(host,port)到套接字:
s.bind(('127.0.0.1', 9999))
print('UDP Server is running...')
print('Waiting for message...')
while True:
# 接收数据,recvfrom()方法返回数据和客户端的地址与端口:
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
客户端代码:
#!/usr/bin/python
# 文件名:udp_client.py
import socket
# 创建一个基于IPv4和UDP协议的socket:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 持续与服务器交互:
while True:
# 获取用户输入
msg = input('Your input:')
if not msg or msg == 'quit':
break
# 无需连接,直接发送数据到服务器Socket绑定的地址:
s.sendto(msg.encode('utf-8'), ('127.0.0.1', 9999))
# 输出服务器返回的消息:
print('From server:',s.recv(1024).decode('utf-8'))
# 套接字关闭
s.close()
打开三个命令行窗口,一个运行服务器端程序,另外两个运行客户端程序,效果如下图:
参考文章:
- python socket编程
- 菜鸟教程-python3网络编程
- 廖雪峰-python3教程