网络编程
ip + port => 网络中某台机器的某个应用程序
- TCP编程
特点:连续的,可靠的,效率较低.
– 服务器
sk = socket.socket()
sk.bind((ip, port))
sk.listen()
conn, address = sk.accept()
conn.send()
conn.recv()
– 客户端
sk = socket.socket()
sk.connect((ip, port))
sk.send()
sk.recv()
– server
单线程
# This is the Server
import socket
# 创建socket对象
sk = socket.socket()
# 默认使用的是TCP编程,使用的端口作为TCP的端口
# sk.bind((IP地址,端口))
sk.bind(("127.0.0.1", 12345))
# 开启监听
sk.listen()
print("服务器准备完毕,等待客户端连接...")
# conn:你和客户端的连接
# address:客户端的IP和端口
conn, address = sk.accept() # 等待客户端的连接
print("连接成功")
while 1:
# 发送数据,数据必须是字节
# conn.send("加密密码".encode("utf-8"))
conn.send(input(">>> ").encode("utf-8"))
# 服务器接受数据
msg = conn.recv(1024)
if msg.decode("utf-8").upper() == "Q":
conn.send("q".encode("utf-8"))
break
print("服务器接受信息为:", msg.decode("utf-8"))
– client
单线程
# This is the Client
import socket
# 创建socket对象
sk = socket.socket()
# 主动连接服务器
sk.connect(("127.0.0.1", 12345))
print("客户端连接成功")
while 1:
# 接收服务器数据,数据大小至多为1024字节
r = sk.recv(1024)
if r.decode("utf-8").upper() == "Q":
sk.send("q".encode("utf-8"))
break
print("客户端接受信息为:", r.decode("utf-8"))
# 向服务器发送消息
# sk.send("解密密码完成".encode("utf-8"))
sk.send(input(">>> ").encode("utf-8"))
– rise
server.py
# This is the Server
import socket
from socket_tools import MyRecvThread, MySendThread
# 创建socket对象
sk = socket.socket()
# 默认使用的是TCP编程,使用的端口作为TCP的端口
# sk.bind((IP地址,端口))
sk.bind(("127.0.0.1", 12345))
# 开启监听
sk.listen()
print("服务器准备完毕,等待客户端连接...")
# conn:你和客户端的连接
# address:客户端的IP和端口
conn, address = sk.accept() # 等待客户端的连接
print("连接成功")
send = MySendThread(conn)
recv = MyRecvThread(conn)
send.start()
recv.start()
client.py
# This is the Client
import socket
from socket_tools import MyRecvThread, MySendThread
# 创建socket对象
sk = socket.socket()
# 主动连接服务器
sk.connect(("127.0.0.1", 12345))
print("客户端连接成功")
send = MySendThread(sk)
recv = MyRecvThread(sk)
send.start()
recv.start()
socket_tools.py
from threading import Thread
class MySendThread(Thread):
def __init__(self, conn):
super(MySendThread, self).__init__()
self.conn = conn
def run(self) -> None:
while 1:
self.conn.send(input(">>> ").encode("utf-8"))
class MyRecvThread(Thread):
def __init__(self, conn):
super(MyRecvThread, self).__init__()
self.conn = conn
def run(self) -> None:
while 1:
msg = self.conn.recv(1024)
print("接受信息为:", msg.decode("utf-8"))
– 黏包
上一次的数据和下一次的数据黏连在一起
解决黏包方法:
每发送一个数据包的时候,拆分成两个包
第一个包:告诉对方,我要发送的数据有多少
第二个包:真实的数据
总结:先告诉对方数据的边界在哪,后接收数据
server.py
import socket
import struct
sk = socket.socket()
sk.bind(("127.0.0.1", 12345))
sk.listen()
print("服务器准备完毕,等待客户端连接...")
conn, address = sk.accept()
print("连接成功")
try:
while 1:
msg = conn.recv(4)
# 解析
# 返回的是元组,数据在[0]
msg_len_bs = struct.unpack("i", msg)[0]
msg_data = conn.recv(msg_len_bs).decode("utf-8")
print(msg_len_bs)
print(msg_data)
except struct.error:
pass
client.py
import socket
import struct
sk = socket.socket()
sk.connect(("127.0.0.1", 12345))
print("客户端连接成功")
for count in range(2):
msg = "信息".encode("utf-8")
# 将整数转换成4位数字节
msg_len_bs = struct.pack("i", len(msg))
sk.send(msg_len_bs)
sk.send(msg)
– 文件上传
客户端向服务器上传图片
server.py
import socket
import struct
import json
def date_recv(recv_conn):
msg_len_bs = recv_conn.recv(4)
msg_len = struct.unpack("i", msg_len_bs)[0]
result = recv_conn.recv(msg_len)
return result
sk = socket.socket()
sk.bind(("127.0.0.1", 12345))
sk.listen()
print("服务器完毕")
conn, address = sk.accept()
print("连接成功")
msg_str = date_recv(conn).decode("utf-8")
# 将 str 转化成 dict 格式
msg_dict = json.loads(msg_str)
with open(msg_dict["name"], mode="wb") as f:
while 1:
msg = date_recv(conn)
if msg == b"1111":
break
f.write(msg)
client.py
import os.path
import socket
import struct
import json
def data_send(send_sk, send_bs):
msg_len = len(send_bs)
send_sk.send(struct.pack("i", msg_len))
send_sk.send(send_bs)
sk = socket.socket()
sk.connect(("127.0.0.1", 12345))
print("客户端连接成功")
# 先发送文件相关信息
data = {
"name": "Kali.png",
"size": os.path.getsize("Kali.png")
}
# 将dict转换成str格式
data_json = json.dumps(data)
data_send(sk, data_json.encode("utf-8"))
f = open("Kali.png", mode="rb")
for bs in f:
data_send(sk, bs)
f.close()
data_send(sk, b"1111")
– 多客户端连接
可以使用一个服务端连接多个客户端
server.py
# 使用socketserver来管理客户端的连接
import socketserver
import time
class MyRequestHandler(socketserver.BaseRequestHandler):
def handle(self) -> None: # 一旦有客户端连接,自动执行handle
# 每个客户端连接,都是一个单独的线程
print("有客户端连接")
conn = self.request # 与客户端的连接
address = self.client_address # 客户端ip地址
while 1:
conn.send(f"你的IP:{address}".encode("utf-8"))
time.sleep(2)
if __name__ == '__main__':
# ThreadingTCPServer中提供IP地址,端口,指定谁来处理请求[类]
# TCPServer 只有前一个进程关闭才可以再进行连接,单线程
# ThreadingTCPServer 每一个客户端是一个线程,多线程
server = socketserver.ThreadingTCPServer(("127.0.0.1", 12345), MyRequestHandler)
server.serve_forever() # 启动
client.py
import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 12345))
while 1:
msg = sk.recv(1024)
print(msg.decode("utf-8"))
- UDP编程
特点:不连续的,不可靠的,效率较高.
不会出现黏包的问题
创建socket的时候需要指定type = socket.SOCK_DGRAM
发送数据 sendto(信息,目标(ip, port))
接受数据 recvfrom(接收信息字节大小)
– server
import socket
# SOCK_DGRAM发送的是(UDP)数据包
sk = socket.socket(type=socket.SOCK_DGRAM)
# UDP的端口
sk.bind(("127.0.0.1", 12345))
# 数据包中有[信息]和[客户端地址]
msg, client = sk.recvfrom(1024)
print(msg.decode("utf-8"), client)
sk.sendto("已收到:{}".format(msg.decode("utf-8")).encode("utf-8"), client)
– client
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
# 发送UDP数据包
# sendto(发送信息, 发送目标[ip, port])
sk.sendto("information".encode("utf-8"), ("127.0.0.1", 12345))
msg, client = sk.recvfrom(1024)
print(msg.decode("utf-8"))