python学习 --网络编程(2):Socket

本文目录

    • 核心技术 socket
      • 搭建socket通信(tcp)
      • 搭建socket通信(udp)
      • 面试题
      • 文件传输

核心技术 socket

搭建socket通信(tcp)

搭建服务端

  1. 导入socket模块
import socket
  1. 创建socket对象
sk = socket.socket() # 创建socket对象 	(买了新手机)
# 如果初始化过程中不传递参数,则默认为tcp协议传输
  1. 绑定IP和端口地址
sk.bind(("172.16.1.36",8080)) # 绑定ip地址,格式为元组(ip,端口)  (办张电话卡)
  1. 监听客户端连接
sk.listen() # 开启监听		(开机)
print("服务器已启动...")
  1. 等待客户端进行连接 accept() 接受连接,是一个阻塞方法,即 TCP 的三次握手
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)
  1. 开始通信

在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")  # 接收客户端发送的消息,做解码处理


  1. 关闭连接
conn.close()	#(挂断电话)
  1. 关闭socket
sk.close()		# (关机)

搭建客户端

  1. 导入socket模块
import socket
  1. 创建socket对象
sk = socket.socket() # 创建socket对象
  1. 开始连接服务器
sk.connect(("172.16.1.38",8080)) # 连接指定服务器,格式为元组(ip,端口)
print("服务器已启动...")
  1. 开始通信

  2. 关闭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()

搭建socket通信(udp)

搭建服务端

  1. 导入socket模块
import socket
  1. 创建socket对象
sk = socket.socket(type=socket.SOCK_DGRAM) # 使用udp协议创建socket对象 
  1. 绑定IP和端口地址
sk.bind(("172.16.1.36",8080)) # 绑定ip地址,格式为元组(ip,端口)
  1. 开始通信,等待客户端发送请求

在udp协议下,不需要和客户端建立专用连接,因此不需要监听阻塞等操作

            msg = sk.recvfrom(1024)
  1. 关闭socket
sk.close()

搭建客户端

  1. 导入socket模块
import socket
  1. 创建socket对象
sk = socket.socket(type=socket.SOCK_DGRAM) # 使用udp协议创建socket对象 
  1. 开始通信
sk.sendto(msg.encode("utf-8"),("172.16.1.42",8080))
  1. 关闭socket
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"))

对于大文件、跨服务器传输来说,可以使用断点续传功能

面试题

黏包现象两种情况

  • 黏包现象一:在发送端,由于两个数据短,发送的时间隔较短,所以在发送端形成包
  • 黏包现象二:在接收端,由于两个数据几乎同时被发送到对方的缓存中,所有在接收端形成了黏包
  • 总结:发送端,包之间时间间隔短或者接收端,接受不及时,就会黏包,核心是因为tcp对数据无边界戳取,不会按照发送的顺序判断

原因

  1. 数据粘包是因为在客户湍/服务器端都会有一个数据缓中区缓中区用来临时保存数据,为了保证能够完整的接收到数据,因此 缓中区都会设置的比较大。
  2. 在收发效据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度,导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包

解决办法

  1. 发送两个包的设置间隔时间
import time
sk.send(b"hello") # 发送第一条数据包
time.sleep(3) # 等待三秒钟
sk.send(b"hello") # 发送第二条数据包
  1. 计算发送接收消息长度
str = "ciskhdfhsugfhsdf"
num = len(str)
sk.send(str(num).encoding("utf-8")) # 提前将长度传送给服务器端
sk.send(str.encoding("utf-8"))
  1. 8字节固定输出

  2. 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)

你可能感兴趣的:(网络,python,学习)