day36(socket套接字)

1、socket是什么?

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。

把复杂的TCP/IP协议族隐藏在Socket接口后面,方便用户调用。

 

2、socket分类

基于文件类型的套接字家族:AF_UMIX

基于网络类型的套接字家族:AF_INET

 

3、TCP套接字工作流程

服务端套接字函数
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() 创建一个与该套接字相关的文件
套接字函数

# 流程案例

# 服务端
# 服务端应该满足的特点:
# 1、一直提供服务
# 2、并发地提供服务

import socket


# 1、socket()创建服务器套接字 == 》买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式协议=》tcp协议

# 2、bind() 把地址绑定到套接字 == 》绑定手机卡
phone.bind(('127.0.0.1',8080)) # 0-65535, 1024以前的都被系统保留使用

# 3、listen() 监听链接 == 》开机
phone.listen(5) # 5指的是半连接池的大小
print('服务端启动完成,监听地址为:%s:%s' %('127.0.0.1',8080))

# 4、accept() 接受客户端请求 ==》等待电话连接请求:拿到电话连接conn
# 加上链接循环
while True:
conn,client_addr=phone.accept()

# 5、通信:收\发消息
while True:
try:
data=conn.recv(1024) # 最大接收的数据量为1024Bytes,收到的是bytes类型
if len(data) == 0:
# 在unix系统洗,一旦data收到的是空
# 意味着是一种异常的行为:客户度非法断开了链接
break
print("客户端发来的消息:",data.decode('utf-8'))
conn.send(data.upper())
except Exception:
# 针对windows系统
break

# 6、close()关闭客户端套接字 ==》关闭电话连接conn(必选的回收资源的操作)
conn.close()

# 7、关机(可选操作)
phone.close()

# 客户端
import socket


#1、socket()创建客户端套接字 ==》买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式协议=》tcp协议

#2、connect() 尝试连接服务器 ==》拨通服务端电话
phone.connect(('127.0.0.1',8080))

#3、通信
while True:
msg=input("输入要发送的消息>>>: ").strip()
if len(msg) == 0:continue
phone.send(msg.encode('utf-8'))
print('======?')
data=phone.recv(1024)
print(data.decode('utf-8'))

分析:当msg = ''时,发生了什么?
如果不加限制条件,会向本机操作系统发送‘空’,系统是不会向服务端发送数据的,会
造成阻塞


#4、关闭连接(必选的回收资源的操作)

phone.close()

 

4、问题及解决方案

有的同学在重启服务端时可能会遇到

这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址造成的

解决方法:

方法一:加入一条socket配置,重用ip和端口

phone=socket(AF_INET,SOCK_STREAM)

phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加

phone.bind(('127.0.0.1',8080))

方法二:直接更改端口

 

5、半连接池

listen(5)维护着一个半连接池,最多只能容纳5个,相当于一个等候室,没有必要非常大,

而accept的作用就是从半连接池取走客户端的请求往下执行

在正常情况下,由于多并发程序的存在,程序执行非常快

在没有并发的情况下,程序会往下执行,在当前处理的请求未终结之前,半连接池内的其它客户端的请求会一直等待

 

6、基于UDP协议的套接字通信

# udp是无链接的,先启动哪一端都不会报错

# 服务端

import socket
# 1、创建套接字
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议=》udp协议
# 2、把地址绑定给套接字
server.bind(('127.0.0.1',8081))
# 3、通信
while True:
data,client_addr=server.recvfrom(1024)
server.sendto(data.upper(),client_addr)

# 4、断开连接
server.close()

 

# 客户端

import socket
# 1、创建套接字
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 流式协议=》tcp协议
# 2、通信
while True:
msg=input('>>>: ').strip()
client.sendto(msg.encode('utf-8'),('127.0.0.1',8081))
res=client.recvfrom(1024)
print(res)
# 3、断开通信
client.close()

注意:1、UDP通信可以发’ 空‘,是因为UDP协议是数据报形式,在通信时经过处理后在系统看来并不为空,因此可以正常发送

   2、UDP通信如果客户端意外断开,不会引起服务端出现异常

 

posted @ 2020-04-20 17:08  东城西月  阅读(88)  评论(0)    收藏  举报