套接字工作流程
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
常用语法:
import socket
socket.socket(socket_family,socket_type,protocal=0)
# socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
# 获取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。如
tcpSock = socket(AF_INET, SOCK_STREAM)
服务端套接字函数
- s.bind() 绑定(主机,端口号)到套接字
- s.listen() 开始TCP监听
- s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
- s.connect() 主动初始化TCP服务器连接
- s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
- s.recv() 接收TCP数据
- s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
- s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
- s.recvfrom() 接收UDP数据
- s.sendto() 发送UDP数据
- s.getpeername() 连接到当前套接字的远端的地址
- s.getsockname() 当前套接字的地址
- s.getsockopt() 返回指定套接字的参数
- s.setsockopt() 设置指定套接字的参数
- s.close() 关闭套接字
面向锁的套接字方法
- s.setblocking() 设置套接字的阻塞与非阻塞模式
- s.settimeout() 设置阻塞套接字操作的超时时间
- s.gettimeout() 得到阻塞套接字操作的超时时间
面向文件的套接字的函数
- s.fileno() 套接字的文件描述符
- s.makefile() 创建一个与该套接字相关的文件
客户端和服务端交互示例:
# 服务端
import socket
test=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
test.bind(('127.0.0.1',8080)) # 绑定的IP和端口
test.listen(5) # 参数表示最大可以挂起的连接数
conn,client_addr=test.accept() # 建立的链接,客户端的链接消息
print(conn)
print(client_addr)
client_data=conn.recv(1024) # 表示最大收取的消息
conn.send(client_data.upper()) # 将消息转换为大写后发回
conn.close() # 关闭链接
test.close() #
# 客户端
import socket
test=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
test.connect(('127.0.0.1',8080))
test.send('hello'.encode('utf-8'))
server_data=test.recv(1024)
print('the server respond:',server_data)
test.close()
防止启动的时候地址端口被占用,未被释放,可在bind前添加test.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
:
import socket
test=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
test.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 在链接异常终止后,再次启动会复用之前的IP端口,防止资源没有释放而产生地址冲突
test.bind(('127.0.0.1',8080)) # 绑定的IP和端口
test.listen(5) # 参数表示最大可以挂起的连接数
conn,client_addr=test.accept() # 建立的链接,客户端的链接消息
while True: #
client_data=conn.recv(1024) # 表示最大收取的消息
conn.send(client_data.upper()) # 将消息转换为大写后发回
conn.close() # 关闭链接
test.close()
提示:当客户端发送空字符串时,服务端是不会收取的,这样就会倒是通讯终止在当前,可以通过控制客户端发送的内容来防止出现此问题。