Python socket 编程

应用程序之间通信的直接通道为 socket 编程接口

消息发送时,通过 socket 编程接口将信息交给操作系统,操作系统的 TCP/IP 协议栈驱动将数据交给通讯硬件驱动,最后让通讯硬件将数据将数据发送到网络上。


1. 使用 socket

导入 socket 库(python 预装了 socket 模块哦)

from socket import *

socket 模块中定义了 socket 类和常用的 method 如 bind,listen,connect,accept,recv,send 等方法

函数的描述:

methodsdescription
socket(AF_INET, SOCK_STREAM)实例化 socket 对象,网络层使用 IP 协议,传输层使用 TCP 协议
bind((IP, PORT))绑定 IP 地址和端口号,参数为元组
recv(BUFLEN)从缓冲区读取 BUFLEN 字节
send()发送数据 (二进制)


2. server

server 为 client 提供服务,因此需要先处于监听状态,收到客户端请求后创建一个新的 socket 对象,并利用这个对象来读取接受缓冲区的内容以及发送数据。


实例化 socket

参数 AF_INET 表示网络层使用 IP 协议

参数 SOCK_STREAM 表示传输层使用 TCP 协议

IP 为空代表绑定本机所有网络接口 IP 地址

BUFLEN 为允许一次从 socket 缓冲区读取的最多字节数

通过 listen 启动监听,参数 8 代表最多接受多少个等待连接的客户端

from socket import *

IP = ''
PORT = 50000
BUFLEN = 512

listenSocket = socket(AF_INET, SOCK_STREAM)
listenSocket.bind((IP, PORT))
listenSocket.listen(8)
print(f'服务端启动成功,在 {PORT} 端口等待客户端连接...')

接受连接请求

socket 在 listen 之后就处于监听状态,未收到客户端请求时处于阻塞状态,收到客户端的请求连接后,返回一个 socket 对象 dataSocket 和对应的 (IP, PORT) 元组:

dataSocket, addr = listenSocket.accept()
print('接受一个客户端连接:', addr)

运行代码时,当有客户端连接时,打印出对应的 IP 和 PORT:

接受一个客户端连接: (‘127.0.0.1’, 53017)


发送和接收数据

通过 while 循环来持续监听客户端是否发送数据,使用 dataSocket 来读取数据和发送数据,分别使用 recv() 方法和 send() 方法,通过 decode 和 code 函数来进行二进制和字符串之间的编码和解码(发送的数据类型只能是 bytes 类型)。

当客户端没有发送数据是,dataSocket.recv(BUFLEN) 处于阻塞状态,否则返回字节流

当客户端断开连接时,dataSocket.recv(BUFLEN) 返回空字符串

while True:
    # 等待接收服务端的消息
    # 若未收到消息,则处于阻塞状态等待
	received = dataSocket.recv(BUFLEN)
	
    # 如果返回空bytes,表示对方关闭了连接
	if not received:
		break
    
	# 读取的字节数据是bytes类型,需要解码为字符串
	info = received.decode()
	print(f'收到消息: {info}')
	dataSocket.send(f'服务端收到了消息: {info}'.encode())

关闭 socket

当客户端断开连接时,为了防止程序占用断开,使用 close() 函数关闭 socket

dataSocket.close()
listenSocket.close()


3. client

客户端程序与服务端类似,而且更简洁,包含创建 socket 对象、连接服务端、发送和接收数据以及断开连接几个部分。


实例化 socket

客户端的 PORT 必须与服务端的端口号一致才能连接成功

客户端通过 socket.connect((IP, PORT)) 请求连接服务端

from socket import *

IP = '127.0.0.1'
SERVER_PORT = 50000
BUFLEN = 512

dataSocket = socket(AF_INET, SOCK_STREAM)

请求连接

客户端一切准备就绪后,可以请求与服务端连接

连接的过程中包含了 TCP 的三次握手,客户端首先发送请求,收到服务端的 ACK 后返回一个 ACK,握手成功!

# 连接服务端socket
dataSocket.connect((IP, SERVER_PORT))

发送和接收数据

客户端通过实例化的 socket 来发送和接收数据,发送接收方式和服务端一样

while True:
    toSend = input('>>> ')
    if toSend =='exit':
        break
        
    # 发送消息,也要编码为 bytes
    dataSocket.send(toSend.encode())

    # 等待接收服务端的消息
    # 若未收到消息,则处于阻塞状态等待
    received = dataSocket.recv(BUFLEN)
    
    # 如果返回空bytes,表示对方关闭了连接
    if not received:
        break
        
    # 打印读取的信息
    print(received.decode())

dataSocket.close()

断开连接后,关闭 socket



4. cmd

查看占用端口的进程

当运行服务端后,可以在 cmd 输入以下命令查看占用 50000 端口的进程:

netstat -aon | findstr "50000"

-a 表示显示所有的连接和监听端口,-o 表示显示相应系统进程的PID,-n 以数字(IP) 的方式显示地址和端口.

通过 PID 杀死 pid 为 xxxx 的进程

taskkill /F /pid xxxx

启动服务端

只运行服务端时:

>>> netstat -aon | findstr "50000"
  TCP    0.0.0.0:50000          0.0.0.0:0              LISTENING       17700

原因是服务端创建了 socket 对象,占用了 50000 端口!


启动客户端

运行服务端后接着运行客户端:

>>> netstat -aon | findstr "50000"
  TCP    0.0.0.0:50000          0.0.0.0:0              LISTENING       17700
  TCP    127.0.0.1:50000        127.0.0.1:55484        ESTABLISHED     17700
  TCP    127.0.0.1:55484        127.0.0.1:50000        ESTABLISHED     15648

增加了两个进程,原因是客户端创建了 socket,连接后服务端又创建了一个 socket 对象!


断开连接
细心的同学会发现,关闭客户端断开连接后还有一个 pid 为 0 的进程,状态为 TIME_WAIT!

>>> netstat -aon|findstr "50000"
  TCP    127.0.0.1:55484        127.0.0.1:50000        TIME_WAIT       0

原因如下:

TCP TIME-WAIT 延迟断开TCP 连接时,套接字对被置于一种称为TIME-WAIT 的状态。这样,新的连接不会使用相同的协议、源 IP 地址、目标 IP 地址、源端口和目标端口,直到经过足够长的时间后,确保任何可能被错误路由或延迟的段没有被异常传送。因此,Time_Wait不是多余的状态,而是为了保证通信的正确性、准确性而存在的。因此,这里PID为0的通信均是已“断开”的曾经被进程使用过的连接,而且还没有释放端口。


完整的代码可以在 github 上拉哦~


REFERENCE:

  1. http://www.python3.vip/tut/py/etc/socket/
  2. https://www.bilibili.com/video/BV1a7411z75u?p=2
  3. https://docs.python.org/zh-cn/3/library/socket.html

END

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值