面试题答案
一键面试连接建立
- 创建Socket:使用
socket
函数创建一个非阻塞的TCP Socket,例如在Python中:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
- 发起连接:调用
connect
方法发起连接。由于是非阻塞模式,该方法会立即返回,返回值为-1
且errno
为EINPROGRESS
表示连接正在进行中。
try:
sock.connect((server_address, server_port))
except socket.error as e:
if e.errno != errno.EINPROGRESS:
raise
- 检测连接状态:使用
select
、poll
或epoll
等I/O多路复用机制检测连接是否建立成功。当Socket可写时,表示连接建立成功。例如在Python中使用select
:
import select
readable, writable, exceptional = select.select([], [sock], [])
if sock in writable:
# 连接建立成功
pass
数据发送
- 发送数据:调用
send
方法发送数据。由于是非阻塞模式,send
可能不会一次性发送完所有数据,需要循环发送。
data = b"Hello, World!"
while data:
try:
sent = sock.send(data)
data = data[sent:]
except socket.error as e:
if e.errno == errno.EAGAIN or e.errno == errno.EWOULDBLOCK:
# 发送缓冲区已满,等待可写事件
readable, writable, exceptional = select.select([], [sock], [])
if sock in writable:
continue
else:
raise
- 处理发送缓冲区已满:当
send
返回-1
且errno
为EAGAIN
或EWOULDBLOCK
时,表示发送缓冲区已满。此时可以使用I/O多路复用机制等待Socket可写,然后继续发送剩余数据。
数据接收
- 接收数据:调用
recv
方法接收数据。由于是非阻塞模式,recv
可能不会一次性接收完所有数据,需要循环接收。
buffer = b""
while True:
try:
chunk = sock.recv(1024)
if not chunk:
break
buffer += chunk
except socket.error as e:
if e.errno == errno.EAGAIN or e.errno == errno.EWOULDBLOCK:
# 没有数据可读,等待可读事件
readable, writable, exceptional = select.select([sock], [], [])
if sock in readable:
continue
else:
raise
- 处理无数据可读:当
recv
返回-1
且errno
为EAGAIN
或EWOULDBLOCK
时,表示没有数据可读。此时可以使用I/O多路复用机制等待Socket可读,然后继续接收数据。
连接关闭
- 正常关闭:调用
shutdown
方法关闭Socket的读写功能,然后调用close
方法关闭Socket。
sock.shutdown(socket.SHUT_RDWR)
sock.close()
- 异常关闭:在连接建立、数据发送或接收过程中,如果捕获到不可恢复的异常,如网络中断等,应及时关闭Socket以释放资源。
try:
# 连接建立、数据收发等操作
pass
except socket.error as e:
# 异常处理
sock.close()
在整个过程中,需要妥善处理各种异常情况,确保程序的稳定性和可靠性。同时,合理使用I/O多路复用机制可以提高程序的性能和并发处理能力。