12-python之网络编程

文章目录

  • 一、简单了解网络
  • 二、基于TCP的socket编程
  • 三、基于UDP的socket编程
  • 四、粘包
  • 五、进度条
  • 六、socketserver多并发TCP连接

一、简单了解网络

协议:两边都商量好的东西
LAN:局域网,在小范围通信没问题,但是如果计算机数量庞大会造成广播风暴,需要划分网段
WAN:广域网,大范围网络通信,通过划分网段实现,通过路由器连接。

物理层:通常指物理网卡
数据链路层:每台计算机唯一的MAC地址
网络层:ip地址,通过划分网段来通信
传输层:提供了端口的协议用来定位应用程序,tcp是连续的,可靠的数据传输,需要等待对方回应,效率低。 UDP是不连续的,不可靠的传输,传过去就不管了,效率高。
应用层:app之间的通信

套接字:也就是socket套接字连接,数据表的封装内容 【目标端口 目标ip 目标mac 数据 源mac 源ip 源端口】

二、基于TCP的socket编程

一、基于TCP的socket编程
socket就是套接字,一种通信协议。
TCP连接:需要先建立起连接,发送数据后要等待对方回复
server端:先开启server端,并监听自己的ip和端口,等待客户端连接
client端:再开启client端,主动去连接server端,一次只能发一条数据

1、基于TCP的socket编程-server端

import socket
# 创建sorckt对象
sk = socket.socket()
# 绑定server端ip和端口,里面是元组
sk.bind(('127.0.0.1',6888))
# 开启监听(监听客户端的连接数,记录有哪些客户端来连接并记录相关ip信息)
sk.listen()
print('等待连接')
conn, address = sk.accept()  # 建立连接,conn是C-S连接状态,address记录客户端ip端口
print('等待成功')

2、基于TCP的socket编程-client端

import socket
# 创建socket对象
sk = socket.socket()
# 连接server端
sk.connect(('127.0.0.1',6888))

3、连接成功后就可以通信了

(1) server端配置(先启动server端)
import socket
# 创建sorckt对象
sk = socket.socket()
# 绑定server端ip和端口,里面是元组
sk.bind(('127.0.0.1',6888))
# 开启监听(监听客户端的连接数)
sk.listen()
# 等待客户端连接,conn是连接状态,address记录客户端ip端口
print('等待连接')
conn, address = sk.accept()
print('连接成功')

while 1:
    # 服务端先收到字节(使用conn收)
    byts = conn.recv(1024)

    # 如果接受的内容是"Q",就退出聊天
    msg = byts.decode('utf-8') # 再把字节转换成中文,并交给变量msg,主要看一下接收到的字节是否是 'Q'
    if msg == "Q":
        break

    # 如果不是Q,再把接收的字节转换成中文打印出来,这里打印的是来自客户端的消息
    print('来自客户端:', byts.decode('utf-8'))

    # 服务端回复消息并转换成字节发送出去(使用conn去法)
    msg = input('>>>:')
    conn.send(msg.encode('utf-8'))
    
conn.close()  # 关闭连接状态
sk.close()    # 关闭socket连接
    
    
    
 
(2) client端配置(后启动client端)
import socket
# 创建socket对象
sk = socket.socket()
# 连接server端
sk.connect(('127.0.0.1',6888))

while 1:
    # 客户端发送消息,并转换成字节(使用socket对象去发)
    msg = input('>>>:')
    sk.send(msg.encode('utf-8'))

    # 如果发送的内容是"Q",就退出聊天
    if msg == "Q":
        break

    # 客户端先收到字节,再转换成中文(使用sorcket收)
    byts = sk.recv(1024)
    print('来自服务端:', byts.decode('utf-8'))
    
sk.close()    # 关闭socket连接

三、基于UDP的socket编程

一、基于UDP的socket编程
UDP连接:只负责发送,对方收没收到不管
server端:先开启server端,并绑定自己的ip和端口
client端:再开启client端,主动去连接server端,可以连续发送多条数据

(1) server端配置
import socket
# 创建socket对象,指定类型为UDP
sk = socket.socket(type=socket.SOCK_DGRAM)
# 绑定ip和端口,里面是元组
sk.bind(('127.0.0.1',6889))

while 1:
    # 接受来自客户端的数据,byts是接收的字节,address存放客户端ip和端口
    byts, address = sk.recvfrom( 1024)

    # 如果收到的是"Q",就退出
    byts_msg = byts.decode('utf-8')  # 先把收到的信息保存
    if byts_msg == "Q":
        break

    # 把收到的字节内容转换成中文
    print('来自客户端:', byts.decode('utf-8'))

    # 给客户端发送数据,address是客户端ip和端口
    msg = input('>>>:')
    sk.sendto(msg.encode('utf-8'), address)
# 关闭socket连接
sk.close() 




(2) client端连接
import socket
# 创建socket对象
sk = socket.socket(type=socket.SOCK_DGRAM)

while 1:
    # 给服务端发送数据,
    msg = input('>>>:')
    sk.sendto(msg.encode('utf-8'), ('127.0.0.1', 6889))

    # 如果发送内容是"Q",就退出
    if msg == "Q":
        break

    # 接收来自服务端的数据
    byts,address = sk.recvfrom(1024)
    print('来自服务端:', byts.decode('utf-8'))

sk.close()

四、粘包

一、粘包
粘包只存在于TCP,UDP没有粘包一说,粘包属于通信bug
大量发送数据时,由于发送数据太快,导致目标主机收到的数据包堆积到一起的,比如第一次发送了‘ABC’,第二次发送了‘123’,目标主机收到的应该是‘ABC,123‘,如果出现粘包收到的就是’ABC123‘

1、解决粘包的核心思想
server: 先把发送信息[ABCD]转换成字节[b’ABCD’]——>统计字节长度4 并统一改成4位数0004 ——> 先发送4位数[0004] ——> 再发送字节[b’ABCD’]
client: 先接收4位数[0004] 并还原成字节长度4 ——> 再接收字节[b’ABCD’] ——> 把字节还原成原始信息[ABCD]

# 用户发送
(1) 比如用户第一次发送了'ABCDEFGHIGKLMN',先转换成字节,字节长度是14个,14是2位数(千、白、十、个),通过format()统一格式化成4位数0014(千、百、十、个)
(2) 用户第二次发送了'123456',先转换成字节,字节长度是6个,6是1位数(千、白、十、个),通过format()统一格式化成4位数0006(千、百、十、个)

# 对方接收
(3) 对方第一次先接收的是一个4位数,拆开是0014,发现字节长度是14个,后收到字节内容b'ABCDEFGHIGKLMN123456',再取前14个字节并把字节转换成原始信息。
(4) 对方第二次接收的依然是4位数,拆开后是0006,发现字节长度是6个,由于第一次已经收到了字节,所以直接把后面的6个字节转换成原始信息就可以了解决粘包的问题了。

2、用函数解决粘包(很lou),主要用于理解

(1) server端(先启动)
import socket
# 创建socket对象
sk = socket.socket()
# 绑定server端地址,里面是元组
sk.bind(('127.0.0.1',6888))
# 开启监听(监听客户端的连接数)
sk.listen()
# 等待客户端连接,conn是C-S连接状态,address存放客户端地址
print('等待连接')
conn,address = sk.accept()
print('连接成功')

# 收信函数,用于解决粘包
def my_recv():
    # 先接收到的是对方传来了一个4位数0009
    msg_four = conn.recv(4)
    # 再把这个4位数还原成实际字节长度9
    msg_len_byts = int(msg_four.decode('utf-8'))
    # 然后再接收转换成字节的信息
    msg_byts = conn.recv(msg_len_byts)
    # 最后把接收到的字节转换成文字('哈哈哈')
    print('来自客户端:',msg_byts.decode('utf-8'))

my_recv()
my_recv()
my_recv()





(2) client端(后启动)
import socket
# 创建socket对象
sk = socket.socket()
# 用于server端建立连接
sk.connect(('127.0.0.1',6888))

# 发信函数,用于解决粘包
def my_send(msg):
    # 先把要发送的信息转换成字节('哈哈哈'转换成字节)
    msg_byts = msg.encode('utf-8')
    # 再统计出字节长度('哈哈哈'的字节长度是9)
    msg_byts_len = len(msg_byts)
    # 然后把字节长度统一转换成4位数(9转换成千位数是 0009)
    msg_four = format(msg_byts_len, '04d')
    # 再然后把4位数(0009)转换成字节发送出去,告诉对方第一次发送的内容是4位数
    sk.send(msg_four.encode('utf-8'))

    # 最后发送转换成字节的信息
    sk.send(msg_byts)

# 输入要发送的内容
my_send(input(">>>:"))
my_send(input(">>>:"))
my_send(input(">>>:"))

3、使用struct() 模块解决粘包(正常解决粘包用法)
struct(‘i’, 数字): i表示转换格式,转换成千位数(千百十个),也就是4位数

3、使用struct() 模块解决粘包(正常解决粘包用法)
struct('i', 数字): i表示转换格式,转换成千位数(千百十个),也就是4位数
<这里可能不完整需要网上自行搜索教程>

使用struct()解决粘包

(1) server端
import socket
import struct
# 创建socket对象
sk = socket.socket()
# 绑定server端ip端口,里面是元组
sk.bind(('127.0.0.1',6888))
# 开启监听(监听客户端的连接数)
sk.listen()
# 等待客户端连接,conn是CS连接状态,address记录客户端ip端口
print('等待连接')
conn,address = sk.accept()
print('连接成功')

# 使用struct解决粘包,收信函数
def my_recv():
    # 先收到的是4位数的包(0003)
    msg_four_len = conn.recv(4)
    # 再还原成实际发信字节的长度('哈哈哈'的字节长度是9)
    msg_byts_len = struct.unpack('i', msg_four_len)[0]
    # 然后把发信的字节接收下来
    msg_byts = conn.recv(msg_byts_len)   # 注意:这里接收的是字节长度,就可以实现接收字节的效果。
    # 最后把发信字节转换成中文
    print(msg_byts.decode('utf-8'))

# 接受内容
my_recv()




(2) client端
import socket
import struct
# 创建socket对象
sk = socket.socket()
# 连接server端
sk.connect(('127.0.0.1',6888))

# 使用struct解决粘包,发信函数
def my_send(msg):
    # 先把要发送的信息转换成字节('哈哈哈'转换成字节)
    msg_byts = msg.encode('utf-8')
    # 再统计字节长度并转换成4位数('哈哈哈'的长度是3,4位数是0003)
    msg_four_len = struct.pack('i', len(msg_byts))
    # 然后把4位数先发送出去,告诉对方第一次发送的是4位数的包
    sk.send(msg_four_len)
    # 最后发送这个转换成字节的数据包
    sk.send(msg_byts)

# 发送消息
my_send(input(">>>:"))

4、客户端发送图片,服务端接受图片

(1) server端,收图片
import socket
import struct
# 创建socket对象
sk = socket.socket()
# 绑定server端ip端口,里面是元组
sk.bind(('127.0.0.1', 6888))
# 开启监听(监听客户端的连接数)
sk.listen()
# 等待客户端连接
print('等待连接')
conn,address = sk.accept()
print('连接成功')

# 先接收4位长度,并转换成图片实际大小(字节)
file_four_len = conn.recv(4)
file_size_len = struct.unpack('i', file_four_len)[0]


"""
接收图片实际长度(如果图片大小超过1024可能会出现接收不完整问题)
file_size = conn.recv(1024)
f = open('upload/吴亦凡.jpg', mode='wb')
f.write(file_size)
"""

# 所以需要一点一点的保存图片
f = open('upload/吴亦凡.jpg', mode='wb')
while file_size_len > 0:   # 如果图片长度不为0
    byts = conn.recv(1024) # 每次接收一部分字节
    f.write(byts)          # 并写入文件中
    file_size_len -= len(byts) # 直到全部接收完毕

print('图片接收完毕')






(2) client端-发图片
import socket
import os
import struct
# 创建socket对象
sk = socket.socket()
# 连接server端
sk.connect(('127.0.0.1', 6888))

# 获取图片大小(字节),并转换成4位长度
file_size = os.path.getsize('tu.jpg')
file_four_len = struct.pack('i', file_size)

# 先把4位长度发送出去
sk.send(file_four_len)

# 再发送图片
f = open('tu.jpg',mode='rb')
for line in f:
    sk.send(line)  # 每次发送一部分字节

print('图片发送完毕')

4.1、连图片名一起发送过去

(1) server端 接收图片
import socket
import struct
import json
import sys
import time

# 创建socket对象
sk = socket.socket()
# 绑定server端ip端口,里面是元组
sk.bind(('127.0.0.1', 6888))
# 开启监听(监听客户端的连接数)
sk.listen()
# 等待客户端连接
print('等待连接')
conn,address = sk.accept()
print('连接成功')

# 先接收4位数,再还原成json字节的长度
json_four_len = conn.recv(4)
file_size_len = struct.unpack('i', json_four_len)[0]

# 然后接收json字符串字节,并把json字符串字节还原成字符串
json_byts = conn.recv(file_size_len)
dic_json = json_byts.decode('utf-8')

# 再把json字符串转换成字典
dic = json.loads(dic_json)

# 保存图片
f = open(f"upload/{dic['file_name']}", mode='wb') # 创建文件并获取文件名
while dic["file_size"] > 0:   # 如果图片长度不为0
    byts = conn.recv(1024) # 每次接收一部分字节
    f.write(byts)          # 并写入文件中
    dic["file_size"] -= len(byts) # 直到全部接收完毕

# 显示进度条
def person(totle, now):
    baifen = (now * 100 // totle)  # 得到一个数字
    sys.stdout.write(f"\r【{baifen * '#'}】 - {baifen}%") # 字符串拼接

now = 0
for i in range(1,12):
    time.sleep(0.8)
    person(500, now)
    now += 50

print('\n图片接收完毕')






(2) client端发送图片
import socket
import os
import struct
import json
# 创建socket对象
sk = socket.socket()
# 连接server端
sk.connect(('127.0.0.1', 6888))

# 获取图片文件名和图片大小
name = 'tu.jpg'
file_name = os.path.basename(name)
file_size = os.path.getsize(name)

# 把图片名和图片大小保存为字典,并转换成json格式(字符串)
dic = {'file_name':file_name, 'file_size':file_size}
dic_json = json.dumps(dic)

# 把json格式的字符串转换成字节
dic_json_byts = dic_json.encode('utf-8')

# 计算json格式字节长度,并转换成4位数
json_four_len = struct.pack('i', len(dic_json_byts))

# 先把4位数发送出去,再发送转换成字节的json字符串
sk.send(json_four_len)
sk.send(dic_json_byts)

# 最后发送图片(图片默认就是字节)
f = open('tu.jpg',mode='rb')
for line in f:
    sk.send(line)  # 每次发送一部分字节

print('图片发送完毕')

五、进度条

1、进度条

import sys
import time

def person(totle, now):
    baifen = (now * 100 // totle)  # 得到一个数字
    sys.stdout.write(f"\r【{baifen * '#'}】 - {baifen}%") # 字符串拼接

now = 0
for i in range(1,12):
    time.sleep(0.8)
    person(500, now)
    now += 50
----------------------------------------
【###########################################################】 - 100%

六、socketserver多并发TCP连接

1、socketserver可以通过有多个客户端连接到server端
有一个客户端连接进来,就启动一个线程

(1) server端配置
import socketserver,time
from socketserver import BaseRequestHandler

class Myserver(BaseRequestHandler):
    def handle(self):             # 客户端默认会连接handle()
        conn = self.request       # 拿到连接状态conn
        while 1:
            time.sleep(1)
            conn.send(b'hello')   # 给客户端发送字节内容

if __name__ == '__main__':
    # 绑定server端ip端口,同时调用Myserver类
    server = socketserver.ThreadingTCPServer(('127.0.0.1', 6888), Myserver)
    server.serve_forever()  #永远执行
    
    
    
(2) 客户端1配置
import socket
sk = socket.socket()    # 建立socket对象
sk.connect(('127.0.0.1', 6888)) # 连接server端

while 1:
    print(sk.recv(1024))    #接收数据
    
(3) 客户端2配置
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 6888))

while 1:
    print(sk.recv(1024))

你可能感兴趣的:(python,python,网络,开发语言)