Python学习之路--网络编程

Posted rssblogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习之路--网络编程相关的知识,希望对你有一定的参考价值。

由于不同机器上的程序要通信,才产生了网络

C/S Client/Server 客户端/服务端

服务端 一直运行 等待服务别人

客户端 寻求服务的时候 才请求服务

B/S Browser/Server 浏览器/服务器

b/s架构是c/s架构的一种

实现通信上有全球唯一的MAC地址

网卡和网线

网卡

通过ip地址就能找到对应的MAC地址  ARP协议

交换机 ---- 多台机器之间的通信问题

广播风暴

网关  局域网中的机器想要访问局域网外的机器,需要通过网关访问

IP地址 和 子网掩码 按位与  得到网段地址

端口 找到的程序

在计算机上 每一个需要网络通信的程序 都会开一个端口

在同一实际只会有一个程序占用一个端口

不可能在同一时间 在同一个计算机有两个端口占用同一个端口

端口的范围: 0--65535  

127.0.0.1 本地的回环地址

ip地址  确定唯一一台机器

端口  确定唯一的一个程序

ip+端口  找到唯一的一台机器上的唯一的一个程序

基于tcp协议的socket

import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #避免服务重启的时候,报address already is in use
sk.bind((127.0.0.1, 8080))
sk.listen()     # 监听
conn, addr = sk.accept()   # 接收到客户端  连接 地址
print(addr)
ret = conn.recv(1024)  #
print(ret)
conn.send(bhi)  # 发送信息 必须传一个bytes类型

conn.close()  # 关闭连接
sk.close()             #  关闭
import socket
sk = socket.socket()
sk.connect((127.0.0.1, 8080))  #拨号

sk.send(bhello)  # 发送
ret =sk.recv(1024)
print(ret)

sk.close()

tcp中的长连接

技术图片
import socket
sk = socket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()
while True:
    conn,addr = sk.accept()
    while True:
        msg = conn.recv(1024).decode(utf-8)
        if msg == bye:
            break
        print(msg)
        info = input(>>>)
        if info == bye:
            conn.send(bbye)
            break
        conn.send(info.encode(utf-8))

    conn.close()
sk.close()
server
技术图片
import socket
sk = socket.socket()
sk.connect((127.0.0.1,8080))
while True:
    msg = input(>>>>>)
    if msg == bye:
        sk.send(bbye)
        break
    sk.send(msg.encode(utf-8))
    ret = sk.recv(1024).decode(utf-8)
    if ret == bye:
        break
    print(ret)
sk.close()
client1
技术图片
import socket
sk = socket.socket()
sk.connect((127.0.0.1,8080))
while True:
    msg = input(client2:>>>>>)
    if msg == bye:
        sk.send(bbye)
        break
    sk.send((client2:+msg).encode(utf-8))
    ret = sk.recv(1024).decode(utf-8)
    if ret == bye:
        break
    print(ret)
sk.close()
client2

 

 

 

UDP

udp的server 不需要进行监听也不需要建立连接

在启动服务之后只能被动得等待客户端发送消息过来

客户端发送消息的同时还会自带地址消息

消息回复的时候不仅需要发送消息,还需要把对方的地址填写上

单个服务端

技术图片
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind((127.0.0.1,8080))
msg,addr = sk.recvfrom(1024)
print(msg.decode(utf-8))
sk.sendto(bbye,addr)
sk.close()
server
技术图片
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = (127.0.0.1,8080)
sk.sendto(bhello,ip_port)
ret,addr = sk.recvfrom(1024)
print(ret.decode(utf-8))
sk.close()
client

多个客户端

技术图片
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind((127.0.0.1,8080))
while True:
    msg,addr = sk.recvfrom(1024)
    print(addr)
    print(msg.decode(utf-8))
    info = input(>>).encode(utf-8)
    sk.sendto(info,addr)

sk.close()
server
技术图片
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = (127.0.0.1,8080)
while True:
    info = input(二哥:)
    info = (\033[32m来自二哥的消息:%s\033[0m%info).encode(utf-8)
    sk.sendto(info,ip_port)
    msg,addr = sk.recvfrom(1024)
    print(msg.decode(utf-8))
sk.close()
client1
技术图片
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
ip_port = (127.0.0.1,8080)
while True:
    info = input(tiger:)
    info = (\033[34m来自tiger的消息:%s\033[0m % info).encode(utf-8)
    sk.sendto(info, ip_port)
    msg,addr = sk.recvfrom(1024)
    print(msg.decode(utf-8))
sk.close()
client2

黏包

同时接受的一条消息没有接收完,下一次执行的时候又多出来;

tcp 会出现黏包现象 不丢包

一,不知道长度

技术图片
import socket
sk = socket.socket()
sk.bind((127.0.0.1,8090))
sk.listen()
conn,addr = sk.accept()
ret = conn.recv(2)
ret2 = conn.recv(10)
print(ret)
print(ret2)
conn.close()
sk.close()
# b‘he‘
# b‘llo‘
server
技术图片
import socket
sk = socket.socket()
sk.connect((127.0.0.1,8090))
sk.send(bhello)

sk.close()
client

二、优化算法

技术图片
import socket
sk = socket.socket()
sk.bind((127.0.0.1,8090))
sk.listen()
conn,addr = sk.accept()
ret = conn.recv(12)
print(ret)
conn.close()
sk.close()

# b‘helloegg‘
server
技术图片
import socket
sk = socket.socket()
sk.connect((127.0.0.1,8090))
sk.send(bhello)
sk.send(begg)
sk.close()
client

多个send小的数据黏在一起 会发生黏包现象,tcp协议内部的优化算法造成的

技术图片
# 基于tcp实现远程执行命令
# 在server端下发命令
import socket
sk = socket.socket()
sk.bind((127.0.0.1,8090))
sk.listen()
conn, addr = sk.accept()
while True:
    cmd = input(>>>)
    conn.send(cmd.encode(utf-8))
    ret = conn.recv(1024).decode(utf-8)
    print(ret)

conn.close()
sk.close()
server
技术图片
#  在client端接收消息并执行
import socket
import subprocess
sk = socket.socket()
sk.connect((127.0.0.1,8090))
while True:
    cmd = sk.recv(1024).decode(gbk)
    ret = subprocess.Popen(cmd,shell=True,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)
    std_out = stdout:+(ret.stdout.read()).decode(gbk)
    std_err = stderr+(ret.stderr.read()).decode(gbk)
    print(std_out)
    print(std_err)
    sk.send(std_out.encode(utf-8))
    sk.send(std_err.encode(utf-8))

sk.close()
client

udp 不会出现黏包现象 丢包

技术图片
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind((127.0.0.1,8090))
msg,addr = sk.recvfrom(1024)
while True:
    cmd = input(>>>)
    if cmd == q:
        break
    sk.sendto(cmd.encode(utf-8),addr)
    msg,addr = sk.recvfrom(1024)
    print(msg.decode(utf-8))
sk.close()
server
技术图片
import socket
import subprocess
sk = socket.socket(type=socket.SOCK_DGRAM)
addr = (127.0.0.1,8090)
sk.sendto(吃了吗.encode(utf-8),addr)
while True:
    cmd, addr = sk.recvfrom(1024)
    ret = subprocess.Popen(cmd.decode(gbk),shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    std_out = stdout:+(ret.stdout.read()).decode(gbk)
    std_err = stderr+(ret.stderr.read()).decode(gbk)
    print(std_out)
    print(std_err)
    sk.sendto(std_out.encode(utf-8),addr)
    sk.sendto(std_err.encode(utf-8),addr)
sk.close()
client

 黏包问题解决方法一

技术图片
import socket
sk = socket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()
conn,addr = sk.accept()
while True:
    cmd = input(>>>>>)
    if cmd == q:
        conn.send(q)
        break
    conn.send(cmd.encode(gbk))
    num = conn.recv(1024).decode(utf-8)
    conn.send(bok)
    res = conn.recv(int(num)).decode(gbk)
    print(res)
conn.close()
sk.close()
server
技术图片
import socket
import subprocess
sk = socket.socket()
sk.connect((127.0.0.1,8080))
while True:
    cmd = sk.recv(1024).decode(gbk)
    if cmd == q:
        break
    res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    std_out = res.stdout.read()
    std_err = res.stderr.read()
    sk.send(str(len(std_out)+len(std_err)).encode(utf-8))
    sk.recv(1024)
    sk.send(std_out)
    sk.send(std_err)
sk.close()
client

send 和 sendto 在超过一定范围的时候会报错 

当要发送大数据的时候,要明确告诉接收方要发送多大的数据,一遍接收方能够准确的接收到所有数据

多用于文件传输过程中

 黏包问题解决方法二

struck模块

该模块能够把一个了下转换成固定长度的bytes

技术图片
import struct
ret = struct.pack(i,4096) #‘i‘代表int,就是即将要把一个数字转换成固定长度的bytes类型
print(ret)
num = struct.unpack(i,ret)
print(num[0])
# b‘\x00\x10\x00\x00‘
# 4096
struck
技术图片
import socket
sk = socket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()
conn,addr = sk.accept()
while True:
    cmd = input(>>>>>)
    if cmd == q:
        conn.send(q)
        break
    conn.send(cmd.encode(gbk))
    num = conn.recv(4)
    num = struct.unpack(i,num)[0]
    res = conn.recv(int(num)).decode(gbk)
    print(res)
conn.close()
sk.close()
server
技术图片
import struct
import socket
import subprocess
sk = socket.socket()
sk.connect((127.0.0.1,8080))
while True:
    cmd = sk.recv(1024).decode(gbk)
    if cmd == q:
        break
    res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    std_out = res.stdout.read()
    std_err = res.stderr.read()
    len_num = len(std_out)+len(std_err)
    nub_byt = struct.pack(i,len_num)
    sk.send(nub_byt)
    sk.send(std_out)
    sk.send(std_err)
sk.close()
client

struck 模块定制报头理论

网络上传输的所有数据 都叫数据包

数据包的里的所有数据 都叫报文

报文里不止有数据 ip地址 端口号 mac地址

所有的报文都有报头

协议 报头 接收多少个字节

字节定制报文 在复杂的应用上会用到

  传输文件的时候

文件的上传下载

技术图片
# 实现文件的上传下载
import socket
import struct
import json
sk = socket.socket()
sk.bind((127.0.0.1,8090))
sk.listen()
buffer = 4096
conn,addr = sk.accept()

head_len = conn.recv(4)
head_len = struct.unpack(i,head_len)[0]
json_head = conn.recv(head_len).decode(utf-8)
head = json.loads(json_head)
filesize = head[filesize]
with open(head[filename],wb) as f:
    while filesize:
        print(filesize)
        if filesize >= buffer:
            content = conn.recv(buffer)
            f.write(content)
            filesize -= buffer
        else:
            content =conn.recv(filesize)
            f.write(content)
            break
conn.close()
sk.close()
server
技术图片
import socket
import os
import json
import struct
sk = socket.socket()
sk.connect((127.0.0.1,8090))
buffer = 4096

head = filepath:rC:\Users\kehu\Desktop,
        filename:r162210409211 冉思思财务报表分析.docx,
        filesize:None
file_path = os.path.join(head[filepath],head[filename])
filesize = os.path.getsize(file_path)
head[filesize] = filesize
json_head = json.dumps(head)  # 字典转成了字符串
bytes_head = json_head.encode(utf-8)  # 字符串转bytes
print(json_head)
print(bytes_head)
head_len = len(bytes_head)
pack_len = struct.pack(i,head_len)
sk.send(pack_len) # 先发送报头长度
sk.send(bytes_head) # 再发bytes类型的报头
with open(file_path,rb) as f:
    while filesize:
        print(filesize)
        if filesize >= buffer:
            content = f.read(buffer)
            sk.send(content)
            filesize -= buffer
        else:
            content = f.read(filesize)
            sk.send(content)
            break
sk.close()
client

 

以上是关于Python学习之路--网络编程的主要内容,如果未能解决你的问题,请参考以下文章

Python学习之路——模块

Python学习之路3?编程风格

Python学习之路:socket网络编程

Python 之路 Day5 - 常用模块学习

Python学习之路——编程语言介绍

Python 之路 Day5 - 常用模块学习