基于TCP协议的套接字编程(socket编程)
什么是Socket呢? 我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,
它把TCP/IP层复杂的操作抽象为几个简单的接口供应层调用已实现进程在网络中通信。
1. 套接字的分类:
AF_UNIN:用在局域网中
AF_INET:用在互联网中
2. 客户端和服务端该如何启动?
我们应该先启动服务端,服务端启动起来之后,等待着客户端来连接,然后接收客户端发来的消息,进行通信
套接字编程流程:
1.1 简易版本的套接字编程
服务端:
import socket
"""
# SOCK_STREAM:使用的是TCP协议
# SOCK_DGRAM:使用的是UDP协议
"""
# AF_INET:指定这个就完事了
# 买了个手机
server = socket.socket() # 实例化得到socket对象
# server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 实例化得到socket对象
# server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 实例化得到socket对象
###2. 服务端绑定信息
# 给手机买了个手机卡
server.bind(("127.0.0.1", 8000))
###3.监听消息
server.listen(3) # 3代表的是半连接池:可以等待的客户端的数量
###4. 接收消息
sock, addr = server.accept() # 代码走到这里会停住,等待接收客户端发来的消息
"""
sock:代表的是当前客户端的连接对象
addr:代表的是客户端的信息(ip+port)
"""
###5.真正的取到客户端发来的消息
data = sock.recv(1024) # 接收的最大数据,字节类型
print("客户端发来的消息:%s" % data)
###6. 服务端个客户端发消息
sock.send(b'hello') # 数据类型必须是字节类型
###7. 断开与客户端之间的连接
sock.close()
###8. 关机
server.close()
客户端:
import socket
"""
# SOCK_STREAM:使用的是TCP协议
# SOCK_DGRAM:使用的是UDP协议
"""
# AF_INET:指定这个就完事了
# 买了个手机
client = socket.socket() # 实例化得到socket对象
# server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 实例化得到socket对象
# server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 实例化得到socket对象
###2.连接服务端
client.connect(("127.0.0.1", 8000))
###3. 给服务端发送消息
client.send(b'helloworld') # 也要发送字节类型
###4. 接收一些服务端发来的消息
data = client.recv(1024) # 一次接收的最大数据量,字节类型
print("服务端发来的消息:%s" % data)
###5. 挂断
client.close()
问题:
可能你在重启服务端时可能会遇到:
这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址,你可以把当前运行占用的停止,然后重新运行或者重用ip和端口
1.2 加上通信循环
服务端:
import socket
"""
# SOCK_STREAM:使用的是TCP协议
# SOCK_DGRAM:使用的是UDP协议
"""
# AF_INET:指定这个就完事了
# 买了个手机
server = socket.socket() # 实例化得到socket对象
# server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 实例化得到socket对象
# server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 实例化得到socket对象
###2. 服务端绑定信息
# 给手机买了个手机卡
server.bind(("127.0.0.1", 8000))
###3.监听消息
server.listen(3) # 3代表的是半连接池:可以等待的客户端的数量
###4. 接收消息
while True:
sock, addr = server.accept() # 代码走到这里会停住,等待接收客户端发来的消息
"""
sock:代表的是当前客户端的连接对象
addr:代表的是客户端的信息(ip+port)
"""
###5.真正的取到客户端发来的消息
data = sock.recv(1024) # 接收的最大数据,字节类型
print("客户端发来的消息:%s" % data)
###6. 服务端个客户端发消息
sock.send(data.upper()) # 数据类型必须是字节类型
###7. 断开与客户端之间的连接
sock.close()
###8. 关机
server.close()
1.3 加上连接循环
服务端:
while True:
sock, addr = server.accept() # 代码走到这里会停住,等待接收客户端发来的消息
"""
sock:代表的是当前客户端的连接对象
addr:代表的是客户端的信息(ip+port)
"""
while True:
try:
###5.真正的取到客户端发来的消息
data = sock.recv(1024) # 接收的最大数据,字节类型
if len(data) == 0:
continue
print("客户端发来的消息:%s" % data)
###6. 服务端个客户端发消息
sock.send(data.upper()) # 数据类型必须是字节类型
except ConnectionResetError as e:
print(e)
break
###7. 断开与客户端之间的连接
sock.close()
###8. 关机
server.close()
客户端:
#2.连接服务端
client.connect(("127.0.0.1", 8000))
while True:
#3. 给服务端发送消息
# 接收用户输入的消息
inp_data = input("请输入发送给服务端消息:")
client.send(inp_data.encode("utf8")) # 也要发送字节类型
###4. 接收一些服务端发来的消息
data = client.recv(1024) # 一次接收的最大数据量,字节类型
print("服务端发来的消息:%s" % data)
基于UDP协议的套接字编程
服务端:
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 数据报协议-》UDP
server.bind(('127.0.0.1', 8080))
while True:
# client_addr:客户端的地址:ip+port
data, client_addr = server.recvfrom(1024) # 接收客户端发来的消息,1024是字节数
print('===>', data, client_addr)
server.sendto(data.upper(), client_addr) # 给客户端发消息
server.close()
客户端:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 数据报协议-》UDP
while True:
msg = input('>>: ').strip() # msg=''
client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
data, server_addr = client.recvfrom(1024)
print(data)
client.close()
黏包现象
client --------------------------------server
9999
1024
9999-1024
黏包现象:其实就是管道里面的数据没有被完全取出来
TCP的特性:流式协议
"""
当客户端发送的数据量很小并且时间间隔很小的时候
它会把数据打包成一个包一次性发送
"""
struct模块
import struct
# 1000 ======> 4
# 1000000 =======> 4
# helloworld ========> 4
"""报头:4个字节"""
# res=struct.pack('i', 1000)
# i:被打包的数据大小是有限制的
# res=struct.pack('q', 100000000000000000000)
# # res=struct.pack('i', 200000000)
# print(len(res)) # 4
#
# # 解包
# res1=struct.unpack('q', res)[0]
# print(res1)