Scoket层
【一】Scoket层在哪
【二】套接字工作流程
-
一个生活中的场景。
-
- 你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。
- 等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。
【0】服务端流程
-
先从服务器端说起。
-
- 服务器端先初始化Socket
- 然后与端口绑定(bind),对端口进行监听(listen)
- 调用accept阻塞,等待客户端连接。
- 在这时如果有个客户端初始化一个Socket
- 然后连接服务器(connect)
-
-
- 如果连接成功,这时客户端与服务器端的连接就建立了。
-
-
- 客户端发送数据请求,服务器端接收请求并处理请求
- 然后把回应数据发送给客户端,客户端读取数据
- 最后关闭连接,一次交互结束
-
socket()模块函数用法
import socketsocket.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
【1】服务端套接字函数
- s.bind() 绑定(主机,端口号)到套接字
- s.listen() 开始TCP监听
- s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
【2】客户端套接字函数
- s.connect() 主动初始化TCP服务器连接
- s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
【3】公共用途的套接字函数
- 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() 关闭套接字
【4】面向锁的套接字方法
- s.setblocking() 设置套接字的阻塞与非阻塞模式
- s.settimeout() 设置阻塞套接字操作的超时时间
- s.gettimeout() 得到阻塞套接字操作的超时时间
【5】面向文件的套接字的函数
- s.fileno() 套接字的文件描述符
- s.makefile() 创建一个与该套接字相关的文件
【三】基于TCP的套接字
【1】方法简介
-
tcp是基于链接的
-
- 必须先启动服务端
- 然后再启动客户端去链接服务端
-
tcp服务端
server = socket() #创建服务器套接字
server.bind() #把地址绑定到套接字
server.listen() #监听链接
inf_loop: #服务器无限循环conn = server.accept() #接受客户端链接comm_loop: #通讯循环conn.recv()/conn.send() #对话(接收与发送)conn.close() #关闭客户端套接字
server.close() #关闭服务器套接字(可选)
- tcp 客户端
client = socket() # 创建客户套接字
client.connect() # 尝试连接服务器
comm_loop: # 通讯循环client.send()/client.recv() # 对话(发送/接收)
client.close() # 关闭客户套接字
【2】打电话模型
-
socket通信流程与打电话流程类似
-
- 我们就以打电话为例来实现一个low版的套接字通信
(1)服务端
import socketip_port = ('127.0.0.1', 9000) # 电话卡
BUFSIZE = 1024 # 收发消息的尺寸
servser = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买手机
servser.bind(ip_port) # 手机插卡
servser.listen(5) # 手机待机conn, addr = servser.accept() # 手机接电话
# print(conn)
# print(addr)
print('接到来自%s的电话' % addr[0])msg = conn.recv(BUFSIZE) # 听消息,听话
print(msg, type(msg))conn.send(msg.upper()) # 发消息,说话conn.close() # 挂电话servser.close() # 手机关机
(2)客户端
import socketip_port = ('127.0.0.1', 9000)
BUFSIZE = 1024
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client.connect_ex(ip_port) # 拨电话client.send('dream is handsome'.encode('utf-8')) # 发消息,说话(只能发送字节类型)feedback = client.recv(BUFSIZE) # 收消息,听话
print(feedback.decode('utf-8'))client.close() # 挂电话
【3】打电话模型升级
- 加上链接循环与通信循环
import socket# 电话卡
ip_port = ('127.0.0.1', 8081)
BUFSIZE = 1024# 买手机
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 手机插卡
server.bind(ip_port)# 手机待机
server.listen(5)# 新增接收链接循环,可以不停的接电话
while True:# 手机接电话conn, addr = s.accept()# print(conn)# print(addr)print('接到来自%s的电话' % addr[0])# 新增通信循环,可以不断的通信,收发消息while True:# 听消息,听话msg = conn.recv(BUFSIZE)# 如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生# if len(msg) == 0:break print(msg, type(msg))# 发消息,说话conn.send(msg.upper())# 挂电话conn.close()# 手机关机
server.close()
- 客户端改进版
import socketip_port = ('127.0.0.1', 8081)
BUFSIZE = 1024
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 拨电话
client.connect_ex(ip_port) # 新增通信循环,客户端可以不断发收消息
while True: msg = input('>>: ').strip()if len(msg) == 0: continue# 发消息,说话(只能发送字节类型)client.send(msg.encode('utf-8'))# 收消息,听话feedback = client.recv(BUFSIZE) print(feedback.decode('utf-8'))# 挂电话
client.close()