Python 网络编程Socket

网络编程

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"))

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值