Python 简明教程

Python - Socket Programming

Python Socket Programming

Socket programming 是一种技术,其中我们让连接在网络中的两个节点之间进行通信,其中服务器节点监听来自客户端节点的传入请求。

在 Python 中, socket 模块用于套接字编程。标准库中的 socket 模块包括在硬件级别进行服务器和客户端之间通信所需的功能。

本模块提供了 BSD 套接字接口的访问权限。它可在所有操作系统上使用,例如 Linux、Windows、MacOS。

What are Sockets?

套接字是双向通信通道的端点。套接字可以在进程内进行通信,也可以在同一电脑上的进程之间或不同区域中的进程之间进行通信。

套接字通过 IP 地址和端口号组合识别。为了开始通信,它应该在两端均正确配置。

connection
ip address

套接字可以用许多不同信道类型执行:Unix 域套接字、TCP、UDP 等。套接字库为处理常见的传输提供了特定类,并为处理其余部分提供了一个通用接口。

套接字编程术语意味着以编程方式设置套接字,使其能够发送和接收数据。

有两种类型的通信协议 −

  1. connection-oriented protocol

  2. connection-less protocol

TCP 或传输控制协议是一种面向连接的协议。数据由服务器以数据包形式传输,并由接收器按照相同传输顺序进行组装。因为通信两端的套接字在开始之前需要设置,所以这种方法的可靠性更高。

UDP 或用户数据报协议是无连接的。该方法不可靠,因为套接字不需要建立任何用于传输数据连接和终止处理。

Python socket Module

socket 模块用于为网络中连接的节点创建和管理套接字编程。 socket 模块提供了一个 socket 类。您需要使用 socket.socket() 构造函数创建套接字。

socket 类的对象代表主机名和端口号对。

Syntax

下面是 socket.socket() 构造函数的语法 -

socket.socket (socket_family, socket_type, protocol=0)

Parameters

  1. family − 默认情况下为 AF_INET。其他值 - AF_INET6(八组四位十六进制数字)、AF_UNIX、AF_CAN(控制器局域网络)或 AF_RDS(可靠数据包套接字)。

  2. socket_type − 应该是 SOCK_STREAM(默认值)、SOCK_DGRAM、SOCK_RAW 或者可能是其他 SOCK_ 常量之一。

  3. protocol − 数字通常为零,可以省略。

Return Type

此方法返回一个套接字对象。

一旦有了套接字对象,你可以使用所需的方法创建客户端或服务器程序。

Server Socket Methods

在服务器上实例化的套接字称为服务器套接字。以下方法可在服务器上的套接字对象上使用 −

  1. bind() method − 此方法将套接字绑定到指定的 IP 地址和端口号。

  2. listen() method − 此方法启动服务器并进入侦听循环,寻找来自客户端的连接请求。

  3. accept() method − 当服务器截获连接请求时,此方法接受它并识别具有其地址的客户端套接字。

要在服务器上创建套接字,请使用以下片段 −

import socket
server = socket.socket()
server.bind(('localhost',12345))
server.listen()
client, addr = server.accept()
print ("connection request from: " + str(addr))

默认情况下,服务器绑定到本地计算机的 IP 地址“localhost”,监听在任意空闲的端口号。

Client Socket Methods

在客户端设置了类似的套接字。它主要向其 IP 地址和端口号处监听的服务器套接字发送连接请求。

connect() method

此方法以一个二元组对象作为参数。这两个成员是服务器的 IP 地址和端口号。

obj=socket.socket()
obj.connect((host,port))

一旦连接被服务器接受,两个套接字对象都可以发送和/或接收数据。

send() method

服务器使用它截获的地址向客户端发送数据。

client.send(bytes)

客户端套接字向与其建立连接的套接字发送数据。

sendall() method

与 send() 类似。但是与 send() 不同,此方法会持续发送数据字节,直到所有数据发送完毕或者出现错误。成功时不返回任何内容。

sendto() method

此方法仅在 UDP 协议的情况下使用。

recv() method

此方法用于检索发送到客户端的数据。对于服务器而言,它使用其请求已被接受的远程套接字。

client.recv(bytes)

recvfrom() method

此方法在 UDP 协议中使用。

Python - Socket Server

要编写 Internet 服务器,我们使用 socket 模块中可用的 socket 函数创建一个 socket 对象。此后使用该 socket 对象来调用其他函数以设置一个 socket 服务器。

现在调用 bind(hostname, port) 函数,为您的服务在给定的主机上指定一个端口。

接下来,调用返回对象的 accept 方法。此方法会一直等待直到某个客户端连接到您指定的端口,然后返回表示对该客户端连接的连接对象。

Example of Server Socket

import socket
host = "127.0.0.1"
port = 5001
server = socket.socket()
server.bind((host,port))
server.listen()
conn, addr = server.accept()
print ("Connection from: " + str(addr))
while True:
   data = conn.recv(1024).decode()
   if not data:
      break
   data = str(data).upper()
   print (" from client: " + str(data))
   data = input("type message: ")
   conn.send(data.encode())
conn.close()

Python - Socket Client

接下来让我们编写一个非常简单的客户端程序,该程序打开一个对给定的本地主机和端口 5001 的连接。使用 Python 的 socket 模块函数创建 socket 客户端非常简单。

socket.connect(hosname, port) 在该端口处向 hostname 打开一个 TCP 连接。一旦您打开一个套接字,就可以像任何 IO 对象一样从中读取内容。完成后,别忘了关闭它,就像你会关闭一个文件一样。

Example of Client Socket

下列代码是一个非常简单的客户端,它连接到给定的主机和端口,从套接字读取任何可用的数据,然后在输入“q”时退出。

import socket
host = '127.0.0.1'
port = 5001
obj = socket.socket()
obj.connect((host,port))
message = input("type message: ")
while message != 'q':
   obj.send(message.encode())
   data = obj.recv(1024).decode()
   print ('Received from server: ' + data)
   message = input("type message: ")
obj.close()
  1. 首先运行服务器代码。它开始侦听。

  2. 然后启动客户端代码。它发送请求。

  3. 请求已接受。已识别客户端地址

  4. 输入一些文本后按 Enter 键。

  5. 打印已接收的数据。将数据发送至客户端。

  6. 收到服务器的数据。

  7. 输入“q”时循环终止。

服务器-客户端交互如下所示:

server client interaction

我们已在本地计算机上使用 socket 模块实现了客户端-服务器通信。若要将服务器和客户端代码放置在网络中的两台不同计算机上,我们需要找到服务器的 IP 地址。

在 Windows 中,可以通过运行 ipconfig 命令来找到 IP 地址。ifconfig 命令是 Ubuntu 上的等效命令。

ipv4 address

使用 IPv4 地址值更改服务器和客户端代码中的 host 字符串,并像以前一样运行它们。

Python File Transfer with Socket Module

下面的程序演示了如何使用套接字通信来将某个文件从服务器传输至客户端。

Server Code

建立连接的代码与之前相同。连接请求被接受后,服务器上的一个文件会以二进制模式打开用于读取,且字节会顺序读取并发送至客户端流,直至到达文件结尾。

import socket
host = "127.0.0.1"
port = 5001
server = socket.socket()
server.bind((host, port))
server.listen()
conn, addr = server.accept()
data = conn.recv(1024).decode()
filename='test.txt'
f = open(filename,'rb')
while True:
   l = f.read(1024)
   if not l:
      break
   conn.send(l)
   print('Sent ',repr(l))
f.close()
print('File transferred')
conn.close()

Client Code

在客户端上,一个新文件会以 wb 模式打开。从服务器接收到的数据流被写入该文件。当流结束时,输出文件会关闭。客户端计算机上会创建一个新文件。

import socket

s = socket.socket()
host = "127.0.0.1"
port = 5001

s.connect((host, port))
s.send("Hello server!".encode())

with open('recv.txt', 'wb') as f:
   while True:
      print('receiving data...')
      data = s.recv(1024)
      if not data:
         break
      f.write(data)

f.close()
print('Successfully received')
s.close()
print('connection closed')

The Python socketserver Module

Python 标准库中的 socketserver 模块是一个用于简化编写网络服务器任务的框架。该模块中包含以下类,它们表示同步服务器:

socketserver module

这些类与相应的 RequestHandler 类结合使用,用于实现服务。BaseServer 是模块中所有 Server 对象的超类。

TCPServer 类使用互联网 TCP 协议,在客户端和服务器之间提供连续数据流。构造函数会自动尝试调用 server_bind() 和 server_activate()。其他参数会传递至 BaseServer 基类。

你必须创建 StreamRequestHandler 类的子类。它提供了 self.rfile 和 self.wfile 属性,用于读写以获取请求数据或将数据返回至客户端。

  1. UDPServerDatagramRequestHandler - 这些类用于 UDP 协议。

  2. DatagramRequestHandlerUnixDatagramServer - 这些类使用 Unix 域套接字;它们在非 Unix 平台上不可用。

Server Code

你必须编写一个 RequestHandler 类。每当连接到服务器时,该类都会被实例化一次,且必须重写 handle() 方法来实现与客户端的通信。

import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
   def handle(self):
      self.data = self.request.recv(1024).strip()
      host,port=self.client_address
      print("{}:{} wrote:".format(host,port))
      print(self.data.decode())
      msg=input("enter text .. ")
      self.request.sendall(msg.encode())

在服务器分配的端口号上,TCPServer 类的对象会调用 forever() 方法,以将服务器置于监听模式并接受来自客户端的传入请求。

if __name__ == "__main__":
   HOST, PORT = "localhost", 9999
   with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
      server.serve_forever()

Client Code

在使用 socketserver 时,客户端代码与 socket 客户端应用程序比较类似。

import socket
import sys

HOST, PORT = "localhost", 9999

while True:
   with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
      # Connect to server and send data
      sock.connect((HOST, PORT))
      data = input("enter text .. .")
      sock.sendall(bytes(data + "\n", "utf-8"))

      # Receive data from the server and shut down
      received = str(sock.recv(1024), "utf-8")
      print("Sent: {}".format(data))
      print("Received: {}".format(received))

在命令提示符终端运行服务器代码。为客户端实例打开多个终端。您可以模拟服务器与多个客户端之间的并发通信。