基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
import socket
server = socket.socket() # 建立服务器
"""
括号内不写参数默认就是基于网络的遵循TCP协议的套接字
"""
server.bind(('127.0.0.1', 8080)) # 设置ip和端口
"""
服务端应该具备的特征
固定的地址
...
127.0.0.1是计算机的本地回环地址 只有当前计算机本身可以访问
"""
server.listen(5) # 开机
"""
半连接池(暂且忽略 先直接写 后面讲)
"""
sock, addr = server.accept() # 等待并接听电话 没有人来就原地等待(程序阻塞)
"""
listen和accept对应TCP三次握手服务端的两个状态
"""
print(addr) # 客户端的地址
data = sock.recv(1024) # 接收客户端消息
print(data.decode('utf8'))
sock.send('你好啊'.encode('utf8')) # 发送消息给客户端
"""
recv和send接收和发送的都是bytes类型的数据
"""
sock.close() # 关闭连接
server.close() # 关闭服务器
import socket
client = socket.socket() # 产生一个socket对象
client.connect(('127.0.0.1', 8080)) # 根据服务端的地址链接
while True:
sc = input("需要发给服务器的消息:").strip()
client.send(sc.encode("utf8")) # 给服务端发送消息
data = client.recv(1024) # 接收服务端回复的消息
print(data.decode('utf8'))
client.close() # 关闭客户端
由于服务器和客户端之间需要不断通信,所以需要对消息的接收和发送循环起来
# 下方为服务器端循环通信,由于是先recv所以会先等待客户端发消息
while True:
data = sock.recv(1024) # 接收客户端消息
print("客户端:" + data.decode('utf8'))
sc = input("输入需要发给客户端的消息:").strip()
sock.send(sc.encode('utf8')) # 发送消息给客户端
# 下方为客户端循环通信,由于是先send所以需要先发消息,然后等待服务器消息
while True:
sc = input("需要发给服务器的消息:").strip()
client.send(b'hello sweet heart!!!') # 给服务端发送消息
data = client.recv(1024) # 接收服务端回复的消息
print(data.decode('utf8'))
当客户端断开后需要让服务器进入accept等待下一个客户端,此时需要使用到异常处理
如果是windows 客户端异常退出之后服务端会直接报错,使用以下方式重新等待连接:
处理方式
异常处理
如果是mac或linux 服务端会接收到一个空消息,使用一下方法重新等待连接:
处理方式
len判断
循环链接的要点在于客户端断开后服务端需要重新等待客户端连接
while True:
try:
data = sock.recv(1024) # 接收客户端消息
print("客户端:" + data.decode('utf8'))
sc = input("输入需要发给客户端的消息:").strip()
sock.send(sc.encode('utf8')) # 发送消息给客户端
except Exception:
sock, addr = server.accept()
print(addr)
半连接池:限制的是同一时刻的请求数,而非连接数,第一次握手成功时客户端会连上半连接池
设置的最大等待人数 >>>: 节省资源 提高效率
py文件默认同一时间只能运行一次 如果想单独分开运行多次
listen(5)
反复重启服务端可能会报错>>>:address in use
这个错在苹果电脑报的频繁 windows频率较少
from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加
会发生黏包的两种情况:
使用struct模块,精确获取数据的大小,避免数据太小,产生黏包
代码如下(示例):
# struct模块
import struct
data1 = 'hello world!'
print(len(data1)) # 12
res1 = struct.pack('i', len(data1)) # 第一个参数是格式 写i就可以了
print(len(res1)) # 4
ret1 = struct.unpack('i', res1)
print(ret1) # (12,)
data2 = 'hello baby baby baby baby baby baby baby baby'
print(len(data2)) # 45
res2 = struct.pack('i', len(data2))
print(len(res2)) # 4
ret2 = struct.unpack('i', res2)
print(ret2) # (45,)