搭建服务端
import socket
sk = socket.socket() # 创建socket对象 (买了新手机)
# 如果初始化过程中不传递参数,则默认为tcp协议传输
sk.bind(("172.16.1.36",8080)) # 绑定ip地址,格式为元组(ip,端口) (办张电话卡)
sk.listen() # 开启监听 (开机)
print("服务器已启动...")
conn, addr = sk.accept() # 监听连接,等待客户端连接,返回值conn为客户端对象,addr为客户端地址 (等待别人打电话)
# conn, addr 结果
#
# ('172.16.1.8', 6188)
传输协议 (方式)
tcp协议: type=socketKind.SOCK_STREAM (默认)
udp协议: type=socket.SOCK_DGRAM
地址参数
laddr=('172.16.1.42', 8080) localhost address --- laddr 本地地址(IP:Port)
raddr=('172.16.1.8', 6188) remote address --- raddr 远程地址(IP:Port)
在tcp协议下,一定是先启动服务器,再启动客户端。服务器等待客户端连接成功,之后至于服务器先发送还是客户端先发送,都不会影响到本次连接
conn, addr = sk.accept()
conn.send(f"你好客户端 @{addr[0]} , 已收到你的请求 ".encode("utf-8")) # 向客户端发送信息,做编码处理
# 默认发送的类型为字节类型,如果发送中文,需要做编解码处理。
# 发送方需要做编码操作("".encode("utf-8")) 接收方需要做解码处理("".decode("utf-8"))
msg = conn.recv(100).decode("utf-8") # 接收客户端发送的消息,做解码处理
conn.close() #(挂断电话)
sk.close() # (关机)
搭建客户端
import socket
sk = socket.socket() # 创建socket对象
sk.connect(("172.16.1.38",8080)) # 连接指定服务器,格式为元组(ip,端口)
print("服务器已启动...")
开始通信
关闭socket
sk.close()
举例:实现一个服务器连接多个客户端
# server
import socket
sk = socket.socket() # 创建socket对象
sk.bind(("172.16.1.42",8080)) # 绑定ip地址,格式为元组(ip,端口)
sk.listen() # 开启监听
print("服务器已启动...")
while 1:
print("等待客户端连接...")
conn, addr = sk.accept()
msg_client = ""
print(f"客户端@{addr[0]}已连接")
while msg_client != "exit(114514)":
msg_client = conn.recv(1024).decode("utf-8")
print(f"收到客户端消息: {msg_client}")
if msg_client == "886":
conn.send("exit(114514)".encode("utf-8"))
print(f"客户端@{addr[0]}已断开")
conn.close()
break
else:
try:
conn.send(f"你好客户端 @{addr[0]} , 已收到你的请求! ".encode("utf-8"))
except ConnectionAbortedError:
print("客户端连接丢失!")
break
except Exception as result:
print(f"未知错误!{result}")
break
else:
conn.send("exit(114514)".encode("utf-8"))
conn.close()
break
sk.close() # 关闭
exit(114514)
# client
import socket
sk = socket.socket() # 创建socket对象
sk.connect(("172.16.1.42",8080)) # 连接指定服务器,格式为元组(ip,端口)
i = 0
while i<100:
msg_client = input("输入发送给服务器的消息...")
# msg_client = '测试'
sk.send(msg_client.encode("utf-8"))
msg_server = sk.recv(1024).decode("utf-8")
if msg_server == "exit(114514)":
print("连接已断开")
break
print(msg_server)
i += 1
sk.close()
搭建服务端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM) # 使用udp协议创建socket对象
sk.bind(("172.16.1.36",8080)) # 绑定ip地址,格式为元组(ip,端口)
在udp协议下,不需要和客户端建立专用连接,因此不需要监听阻塞等操作
msg = sk.recvfrom(1024)
sk.close()
搭建客户端
import socket
sk = socket.socket(type=socket.SOCK_DGRAM) # 使用udp协议创建socket对象
sk.sendto(msg.encode("utf-8"),("172.16.1.42",8080))
sk.close()
举例:使用UDP协议
# server
import socket
sk = socket.socket(type=socket.SOCK_DGRAM) # 使用udp协议
# 绑定IP端口
sk.bind(("172.16.1.42",8080))
print("服务器已启动...")
while 1:
# 直接发送
msg, addr = sk.recvfrom(1024)
msg = msg.decode("utf-8")
if msg == "exit()":
sk.sendto("exit()".encode("utf-8"),addr)
break
print(msg)
print(addr)
sk.sendto(f"已收到客户端@{addr},服务器已接收{msg}".encode("utf-8"),addr)
sk.close()
import socket
# client
sk = socket.socket(type=socket.SOCK_DGRAM)
while 1:
msg = input("...")
sk.sendto(msg.encode("utf-8"),("172.16.1.42",8080))
msg, addr = sk.recvfrom(1024)
if msg.decode("utf-8") == "exit()":
sk.close()
break
print(msg.decode("utf-8"))
对于大文件、跨服务器传输来说,可以使用断点续传功能
黏包现象两种情况
原因
解决办法
import time
sk.send(b"hello") # 发送第一条数据包
time.sleep(3) # 等待三秒钟
sk.send(b"hello") # 发送第二条数据包
str = "ciskhdfhsugfhsdf"
num = len(str)
sk.send(str(num).encoding("utf-8")) # 提前将长度传送给服务器端
sk.send(str.encoding("utf-8"))
8字节固定输出
struct模块
# server
import json
import socket
sk = socket.socket() # 创建socket对象
sk.bind(("172.16.1.12",8080)) # 绑定ip地址,格式为元组(ip,端口)
sk.listen() # 开启监听
print("服务器已启动...")
conn, addr = sk.accept() # 监听连接,等待客户端连接,返回值conn为对象,addr为客户端地址
msg = conn.recv(10240).decode("utf-8")
dic = json.loads(msg)
print(dic)
if dic["opt"] == "upload":
filename = dic["filename"]
# 文件写入
with open(f"upload\\{filename}","w",encoding="utf-8") as file:
file.write(dic["content"])
conn.close()
sk.close()
# client
import json
import os.path
import socket
sk = socket.socket()
sk.connect(("172.16.1.12",8080))
menu = {1:"upload",2:"download"}
for k,v in menu.items():
print(k,v)
num = input("输入操作...")
if num == "1":
# 提前准备一个字典接受文件的参数,将文件内容放在字典里
dic = {"opt":menu.get(int(num)),"filepath":None,"content":None}
filepath = input("请输入文件的绝对路径...")
# 得到文件名称
filename = os.path.basename(filepath)
# # 得到文件内容(第一版)
# file = open(filepath)
# content = file.read()
# file.close()
# # 得到文件内容(第二版,容错)
# try:
# file = open(filepath)
# content = file.read()
# except Exception as result:
# pass
# finally:
# file.close()
# 得到文件内容(第三版)
with open(filepath,encoding="utf-8") as file: # with 可以在文件流未完成或失败时依然可以关闭文件流
content = file.read()
print(content)
dic["filename"] = filename
dic["content"] = content
# json处理
str_dic = json.dumps(dic) # 将对象转换为json格式
# 将数据发送至服务器
sk.send(str_dic.encode("utf-8"))
print(str_dic)