一.什么是socket?
socket是应用层与TCP/IP协议族通信的中间软件抽象层,她是一组接口.在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,为用户提供一组简单的接口.
二.套接字(socket)分类?
基于文件类型的套接字家族
AF_UNLX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进行运行在同一机器,可以通过访问同一文件系统间接完成通信
基于网络类型的套接字家族
AF_INET
AF_INET是运用最广泛的一个,大部分时候我们只关心网络编程,所以大部分时候我们只使用AF_INET
三.套接字(socket)工作流程?
先从服务端说起,服务端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接.这个时候客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务端的连接就建立了.客户端发送数据,服务端接受请求并处理数据,然后回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互完成.
服务端套接字函数
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() 创建一个与该套接字相关的文件
四.基于TCP的套接字
TCP是基于连接的,不许先启动服务端,然后再启动客户端去连接服务端
TCP服务端:
import socket
server = socket.socket() #买手机
# 默认得到是一个TCP的socket
# server = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
# 两行代码的效果是一样的
# socket的家族 AF_INET
# socket的类型 SOCK_STREAM 对应的是TCP,SOCK_DGRAM 对应的是UDP
# 创建基于UDP的socket
# server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
server.bind(('192.168.12.207',4396)) #插手机卡
server.listen() #开始待机
#连接循环,可以不断接受新连接
while True:
client,address = server.accept()
#通讯循环可以不断收发数据
while True:
try:
#如果windows系统 对方强行关闭连接,会抛出异常
#如果linux系统 不会抛出异常 会死循环收到空数据包
data = client.recv(1024)
if not data:
client.close()
break
print('来自客户端的数据:%s'%data.decode('utf-8'))
client.send(data)
except ConnectionResetError:
print('客户端异常关闭连接')
client.close()
break
TCP客户端:
import socket
client = socket.socket()
client.connect(('192.168.12.207',4396))
while True:
msg = input('请输入>>:').strip()
client.send(msg.encode('utf-8'))
data = client.recv(1024)
print('收到服务器的数据:%s'%data.decode('utf-8'))
半连接池
当服务器在响应了客户端的第一次请求后会进入等待状态,等待客户端发送的恢复信息,这个时候这个连接就称为半连接.
产生半连接的两种情况:1.客户端无法返回ACK信息 2.服务器来不及处理客户端的连接请求
有一种攻击叫做syn洪水攻击,就是不断产生半连接,使服务器无法处理请求.
半连接池其实就是一个容器 系统会自动将半连接放入这个容器中,可以避免半连接过多而造成资源耗光.
在server.listen()括号里面加一个数字,这个数字就是半连接池的容量,最大为5
五.基于UDP的套接字
UDP是无链接的,先启动哪一端都不会报错
UDP协议采取的方法是与TCP完全不同,其根本不关心对方是否接受数据,甚至不关心对方的地址是否有效,只要将数据报发送到网络,便什么都不管了.
优点:由于不需要传输确认信息,所以传输效率高于TCP协议
缺点:传输数据可能不完整
场景:视频聊天,语音聊天等,不要求数据完整性,但是对传输速度要求高
UDP服务器:
import socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
server.bind(('192.168.12.207',4396))
while True:
data,addr = server.recvfrom(1024)
print('收到来自%s的消息%s'%(addr[0],data.decode('utf-8')))
server.sendto(data.upper(),addr)
UDP客户端:
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
data = input('请输入>>:').encode('utf-8')
client.sendto(data,('192.168.12.207',4396))
d,addr = client.recvfrom(1024)
print(d.decode('utf-8'))