外观
网络编程
socket套接字简介
使用套接字(socket)进行网络通信的步骤如下:
一、实例化服务器套接字和客户端套接字对象。
二、服务器套接字首先使用 bind() 方法绑定 IP 地址和端口,然后使用 listen() 方法进行监听。
三、客户端使用 connect() 方法请求连接到服务器,而服务器使用 accept() 方法请求接受来自客户端的连接。
四、现在可以进行数据的互相发送了。假设服务器想要向客户端发送数据,那么服务器的一个进程首先使用 send() 方法向客户端发送数据。然后客户端需要有一个进程来接收来自服务器的数据,这样就完成了一个数据的传递。由于服务器和客户端都是多进程的,因此可以频繁地进行数据传输。
五、最后是断开连接。客户端使用 close() 方法关闭套接字,服务器也使用 close() 方法关闭套接字,因为它们都是 socket 对象。服务器关闭之后就不会再使用 accept() 方法接收其他连接了。
socket模块常用方法
s.bind(): 将地址(IP 地址和端口号)绑定到套接字。在 AF_INET 地址族中,地址以(ip, port)的元组形式表示。s.listen(backlog): 开启 TCP 监听。backlog参数指定操作系统在拒绝连接之前允许挂起的最大连接数,最少为 1,大多数情况下为 5。s.accept(): 被动接收 TCP 客户端连接,并以阻塞方式等待连接请求。该方法返回已经建立连接的套接字对象。s.connect(address): 主动向 TCP 服务器发起连接。address是以(ip, port)元组表示的地址。如果连接失败,会抛出socket.error错误。s.recv(bufsize[, flag]): 接收 TCP 数据。数据以字符串形式返回,需要解码。bufsize参数指定要接收的最大数据量,flag提供与数据相关的信息,通常可省略。s.send(bytes): 发送 TCP 数据。数据以字节码形式发送,如果是字符串需要进行编码。返回值是发送的字节数,可能小于发送信息的大小。s.sendall(bytes): 完整发送 TCP 数据。在返回之前尝试发送所有数据,成功返回None,错误则抛出异常。s.recvfrom(bufsize): 接收 UDP 数据。与recv()类似,但返回的是(data, addr)元组,其中data是返回的数据,addr是发送数据的套接字地址。s.sendto(bytes, address): 发送 UDP 数据。address是以(ip, port)形式表示的元组,指定远程地址。返回值是要发送的字节数。s.close(): 关闭套接字。
TCP程序
在运行 TCP 程序时,通常的顺序是这样的:
首先,服务器先运行。服务器需要绑定 IP 地址和端口,并设置连接的最大数量。
然后,客户端运行。客户端尝试连接服务器。
一旦连接建立成功,服务器和客户端之间就可以进行通信了。
最后,需要关闭服务器和客户端。
常用的是 socket 模块的 socket() 方法。这个方法有两个参数:
第一个参数是
AddressFamily,它代表进行通信的设备。如果是AF_INET,则代表两个计算机之间的通信;如果是AF_UNIX,则代表自己通信。第二个参数是
Type,它代表套接字类型。如果是SOCK_STREAM,代表 TCP 协议;如果是SOCK_DGRAM,代表 UDP 协议。
通过调用 socket() 方法获得一个套接字对象后,就可以使用这个套接字对象进行网络编程了。
浏览器连接到Python服务器
连接步骤请看代码的注释。此段代码运行之后浏览器中输入127.0.0.1:8998后就可以看到浏览器中接收到了Hello World。
# 此文件作为服务器 让浏览器输入连接访问此服务器并尝试通信 同时本文件也是网络编程的第一步
import socket # 导入socket模块
socket = socket.socket() # 使用socket模块的socket()函数创建
socket.bind(('127.0.0.1', 8998)) # 绑定IP地址和端口号
socket.listen(2) # 设置最大监听数量
conn, addr = socket.accept() # 使用套接字对象的accept()方法接收来自客户端的连接 该方法返回一个元组 第一个是连接对象 第二个是地址
conn.sendall(b'HTTP/1.1 200 OK\r\n\r\nHello World!') # 向客户端发送信息
message = conn.recv(1024).decode() # 接收来自浏览器的返回信息 若不接收则浏览器会提示服务器拒绝连接
print(message) # 打印浏览器返回的信息
conn.close() # 关闭连接对象
socket.close() # 关闭套接字
# 最后提示一下 要先运行此文件启动服务器 然后才能打开浏览器连接此服务器 地址是127.0.0.1:8998然后控制台的输出结果如下:
GET / HTTP/1.1
Host: 127.0.0.1:8998
Connection: keep-alive
sec-ch-ua: "Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9编写客户端和服务器
TCP 的优点在于传输质量高、安全可靠,因此被广泛使用。
TCP 程序的运行顺序通常如下:
- 服务器先运行。服务器需要绑定 IP 地址和端口,并设置连接的最大数量。
- 客户端运行。客户端尝试连接服务器。
- 连接成功后,服务器和客户端之间可以进行通信。
- 最后,需要关闭服务器和客户端。
常用的是 socket 模块的 socket() 方法。这个方法有两个参数:
- 第一个参数是
AddressFamily,它代表进行通信的设备。如果是AF_INET,则代表两个计算机之间的通信;如果是AF_UNIX,则代表自己通信。 - 第二个参数是
Type,它代表套接字类型。如果是SOCK_STREAM,代表 TCP 协议;如果是SOCK_DGRAM,代表 UDP 协议。
通过调用 socket() 方法获得一个套接字对象后,就可以使用这个套接字对象进行网络编程了。
具体请看代码:
# server.py
# 此文件演示如何使用TCP套接字实现网络通信 本文件为服务器 先运行
from socket import socket as soc # 导入模块使用套接字
ip = '127.0.0.1' # ip地址 实现本地计算机通信就用这个ip
port = 8080 # 端口
s = soc() # 创建套接字对象
s.bind((ip, port)) # 绑定IP地址和端口号
s.listen(1) # 设置最大连接数量
print('服务器等待连接...')
conn, addr = s.accept() # 建立连接 该方法返回一个元组 第一个元素是已经实现连接的套接字对象 第二个元素是套接字地址 可以用于发送信息
print('服务器连接成功\ns的信息:%s\nconn的信息:%s\naddr的信息:%s\n------' % (s, conn, addr))
conn.send(b'Hello!') # 使用已经连接的套接字对象的sendto()方法发送信息 第一个参数是字节码 第二个参数是要发送的地址
message = conn.recv(1024).decode() # 使用连接对象的recv()方法接收信息 参数是接收的最大字节数 由于接收过来的是字节码 因此还需要解码
print('服务器接收到来自客户端的消息: %s' % message)
conn.close()
s.close() # 关闭套接字# client.py
# 客户端 此文件后运行
from socket import socket as soc # 导入模块
ip = '127.0.0.1' # ip地址
port = 8080 # 端口号
s = soc() # 创建套接字对象
s.connect((ip, port)) # 客户端直接使用connect()方法即可 参数是一个元组 第一个元素是IP地址 第二个元素是端口号
print('连接成功 此时套接字的信息:%s' % s)
message = s.recv(1024).decode() # 接收消息 和服务器一样
print('接收到来自服务器的消息: %s' % message)
s.send(b'Hello!') # 也可以使用s.send('Hello').encode()发送
s.close() # 关闭套接字UDP
UDP 程序相比于 TCP 程序来说,设置连接方式更加简单,因为 UDP 是无连接的,不需要进行连接的建立和维护。
在使用 socket 对象时,只需要改变参数即可将连接方式设置为 UDP。具体而言,将套接字类型参数 Type 设置为 SOCK_DGRAM,即可使用 UDP 协议进行通信。
UDP 程序除了等待传入的连接外,几乎不需要做其他工作。这是因为 UDP 是无连接的,数据包之间的传输不需要建立连接,因此无需像 TCP 那样进行连接的建立和维护,也不需要像 TCP 那样处理连接的中断和重连。UDP 只需简单地发送和接收数据包,因此通常更加轻量和高效。
# server.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 服务器第一步 创建套接字对象 注意参数
s.bind(('127.0.0.1', 1234))
print('绑定端口完成')
data, addr = s.recvfrom(1024) # 第二步 接收数据 返回两个值:数据包和地址
print('服务器接收:%s' % data.decode())
message = 'Hello'
s.sendto(message.encode(), addr) # 第三步 发送数据
# 此时就用到了前面的地址 也就是说这个recvfrom()起到连接作用
print('服务器发送:%s' % message)
s.close()# client.py
import socket # 导入模块
ip = '127.0.0.1'
port = 1234
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 客户端第一步 创建套接字对象
message = 'Hi!'
s.sendto(message.encode(), (ip, port)) # 第二步 发送消息
print('客户端发送:%s' % message)
print('客户端接收:%s' % s.recvfrom(1024)[0].decode()) # 接收消息
s.close()