Python - 套接字编程



Python 套接字编程

**套接字编程**是一种技术,通过它可以在网络中连接的两个节点之间进行通信,其中服务器节点监听来自客户端节点的传入请求。

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

此模块提供对 BSD 套接字接口的访问。它适用于所有操作系统,例如 Linux、Windows、MacOS。

什么是套接字?

套接字是双向通信通道的端点。套接字可以在一个进程内、同一台机器上的进程之间或不同大陆上的进程之间进行通信。

套接字由 IP 地址和端口号的组合标识。它应该在两端正确配置才能开始通信。

connection IP_address

套接字可以在许多不同的通道类型上实现:Unix 域套接字、TCP、UDP 等。套接字库提供用于处理常用传输的特定类,以及用于处理其余传输的通用接口。

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

有两种类型的通信协议:

  • 面向连接的协议

  • 无连接协议

TCP(传输控制协议)是一种面向连接的协议。服务器以数据包的形式传输数据,接收方按传输顺序组装数据。由于通信两端的套接字需要在开始之前设置,因此这种方法更加可靠。

UDP(用户数据报协议)是非连接的。这种方法不可靠,因为套接字不需要建立任何连接和终止过程来传输数据。

Python socket 模块

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

**socket** 类的一个对象表示主机名和端口号对。

语法

以下是 socket.socket() 构造函数的语法:

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

参数

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

  • socket_type − 应为 SOCK_STREAM(默认值)、SOCK_DGRAM、SOCK_RAW 或其他 SOCK_ 常量之一。

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

返回类型

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

获得套接字对象后,可以使用所需的方法创建客户端或服务器程序。

服务器套接字方法

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

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

  • listen() 方法 − 此方法启动服务器并进入监听循环,查找来自客户端的连接请求。

  • accept() 方法 − 当服务器拦截连接请求时,此方法接受请求并使用其地址识别客户端套接字。

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

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

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

客户端套接字方法

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

connect() 方法

此方法将一个包含两个项目的元组对象作为参数。这两个项目是服务器的 IP 地址和端口号。

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

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

send() 方法

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

client.send(bytes)

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

sendall() 方法

类似于 send()。但是,与 send() 不同的是,此方法会继续从字节中发送数据,直到所有数据发送完毕或发生错误为止。成功时不返回任何值。

sendto() 方法

此方法仅用于 UDP 协议。

recv() 方法

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

client.recv(bytes)

recvfrom() 方法

此方法用于 UDP 协议。

Python - 套接字服务器

要编写 Internet 服务器,我们使用 socket 模块中提供的 socket 函数来创建一个套接字对象。然后使用套接字对象调用其他函数来设置套接字服务器。

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

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

服务器套接字示例

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 - 套接字客户端

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

**socket.connect(hosname, port)** 在端口上的主机名打开一个 TCP 连接。打开套接字后,您可以像任何 IO 对象一样从中读取数据。完成后,请记住关闭它,就像关闭文件一样。

客户端套接字示例

以下代码是一个非常简单的客户端,它连接到给定的主机和端口,从套接字读取任何可用数据,然后在输入“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()
  • 首先运行服务器代码。它开始监听。

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

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

  • 输入一些文本并按 Enter 键。

  • 接收到的数据已打印。将数据发送到客户端。

  • 已收到来自服务器的数据。

  • 输入“q”时循环终止。

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

server_client_interaction

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

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

ipv4_address

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

使用 Socket 模块进行 Python 文件传输

以下程序演示如何使用套接字通信将文件从服务器传输到客户端。

服务器代码

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

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()

客户端代码

在客户端,一个新文件以 **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')

Python socketserver 模块

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

socketserver_module

这些类与相应的 RequestHandler 类一起工作以实现服务。BaseServer 是模块中所有 Server 对象的超类。

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

您还必须创建一个 **StreamRequestHandler** 类的子类。它提供 self.rfile 和 self.wfile 属性来读取或写入以获取请求数据或将数据返回给客户端。

  • **UDPServer** 和 **DatagramRequestHandler** − 这些类用于 UDP 协议。

  • **DatagramRequestHandler** 和 **UnixDatagramServer** − 这些类使用 Unix 域套接字;它们在非 Unix 平台上不可用。

服务器代码

您必须编写一个 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()

客户端代码

使用 socketserver 时,客户端代码与套接字客户端应用程序大致相同。

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))

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

服务器 客户端-1 客户端-2

D:\socketsrvr>python myserver.py

127.0.0.1:54518 wrote

from client-1

enter text ..

hello

127.0.0.1:54522 wrote

how are you

enter text ..

fine

127.0.0.1:54523 wrote

from client-2

enter text ..

hi client-2

127.0.0.1:54526 wrote

good bye

enter text ..

bye bye

127.0.0.1:54530 wrote

thanks

enter text ..

bye client-2

D:\socketsrvr>python myclient.py

enter text .. .

from client-1

Sent

from client-1

Received: hello

enter text .. .

how are you

Sent

how are you

Received: fine

enter text .. .

good bye

Sent: good bye

Received: bye bye

enter text .. .

D:\socketsrvr>python myclient.py

enter text .. .

from client-2

Sent

from client-2

Received: hi client-2

enter text .. .

thanks

Sent: thanks

Received

bye client-2

enter text .. .

广告