百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

网络编程——服务器篇 服务器编程用什么软件

liebian365 2024-10-27 13:14 10 浏览 0 评论

目录

一、客户端实现

二、单进程服务器

2.1 单进程实现

2.2 单进程非阻塞实现

2.3 TCP服务器(select版)

2.4 epoll版服务器实现

三、多进程服务器和多线程服务器

四、协程

4.1 协程的生成器实现

4.2 协程的greenlet实现

4.3 协程的gevent实现

4.3.1 gevent的使用

4.3.2 gevent的切换执行

4.3.3 gevent的服务器实现

一、客户端实现

客户端比较简单,并且适用于与不同服务器通信,代码如下:

#coding=utf-8

from socket import *

import random

import time

serverIp = raw_input("请输?服务器的ip:")

connNum = raw_input("请输?要链接服务器的次数(例如1000):")

g_socketList = []

for i in range(int(connNum)):

s = socket(AF_INET, SOCK_STREAM)

s.connect((serverIp, 7788))

g_socketList.append(s)

print(i)

while True:

for s in g_socketList:

s.send(str(random.randint(0,100)))

# ?来测试?

#time.sleep(1)

二、单进程服务器

2.1 单进程实现

Linux服务器开发学习视频资料,包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等,需要知识技术学习视频文档资料的朋友可以后台私信【架构】获取

单进程完成一个tcp服务器,同时只能为一个客户端服务。

from socket import *

serSocket = socket(AF_INET, SOCK_STREAM)

# 重复使?绑定的信息,若服务器先close,则不用等待2MSL,可以直接绑定下一个客户端

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

serSocket.listen(5)

while True:

print('-----主进程, , 等待新客户端的到来------')

newSocket,destAddr = serSocket.accept()

print('-----主进程, , 接下来负责数据处理[%s]-----'%str(destAddr))

try:

while True:

recvData = newSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(destAddr), recvData))

else:

print('[%s]客户端已经关闭'%str(destAddr))

break

finally:

newSocket.close()

serSocket.close()

2.2 单进程非阻塞实现

上面单进程实现同时只能为一个服务端服务,如果第二个while中不阻塞,则可以实现多用户同时服务。代码如下:

#coding=utf-8

from socket import *

import time

# ?来存储所有的新链接的socket

g_socketList = []

def main():

serSocket = socket(AF_INET, SOCK_STREAM)

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

#可以适当修改listen中的值来看看不同的现象

serSocket.listen(1000)

#将套接字设置为?堵塞

#设置为?堵塞后, 如果accept时, 恰巧没有客户端connect, 那么accept会

#产??个异常, 所以需要try来进?处理

serSocket.setblocking(False)

while True:

#?来测试

#time.sleep(0.5)

try:

newClientInfo = serSocket.accept()

except Exception as result:

pass

else:

print("?个新的客户端到来:%s"%str(newClientInfo))

newClientInfo[0].setblocking(False)

g_socketList.append(newClientInfo)

# ?来存储需要删除的客户端信息

needDelClientInfoList = []

# 为列表中每个客户端服务

for clientSocket,clientAddr in g_socketList:

try:

recvData = clientSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(clientAddr), recvData))

else:

print('[%s]客户端已经关闭'%str(clientAddr))

clientSocket.close()

g_needDelClientInfoList.append((clientSocket,clientAddr))

except Exception as result:

pass

for needDelClientInfo in needDelClientInfoList:

g_socketList.remove(needDelClientInfo)

if __name__ == '__main__':

main()

2.3 TCP服务器(select版)

tcp/ip学习资料获取后台私信【tcp/ip】

在非阻塞版本中使用for循环为列表中的每个客户端服务,而select版是通过调用select函数直接返回列表中接收到数据的socket,不必循环遍历。

优点:几乎所有平台都支持,有良好的跨平台性。

缺点:select的?个缺点在于单个进程能够监视的?件描述符的数量存在最?限制,在Linux上?般为1024, 可以通过修改宏定义甚?重新编译内核的?式提升这?限制, 但是这样也会造成效率的降低。

?般来说这个数? 和系统内存关系很?, 具体数? 可以cat /proc/sys/fs/filemax查看。 32位机默认是1024个。 64位机默认是2048.个。对socket进?扫描时是依次扫描的, 即采?轮询的?法, 效率较低。

当套接字?较多的时候, 每次select()都要通过遍历FD_SETSIZE个Socket来完成调度, 不管哪个Socket是活跃的, 都遍历?遍。 这会浪费很多CPU时间。

select函数解释如图:

select版tcp服务器代码如下:

import select

import socket

import sys


server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.bind(('', 7788))

server.listen(5)

inputs = [server, sys.stdin]

running = True

while True:

# 调用 select 函数,阻塞等待

readable, writeable, exceptional = select.select(inputs, [], [])

# 数据抵达,循环

for sock in readable:

# 监听到有新的连接

if sock == server:

conn, addr = server.accept()

# select 监听的socket

inputs.append(conn)

# 监听到键盘有输入

elif sock == sys.stdin:

cmd = sys.stdin.readline()

running = False

break

# 有数据到达

else:

# 读取客户端连接发送的数据

data = sock.recv(1024)

if data:

sock.send(data)

else:

# 移除select监听的socket

inputs.remove(sock)

sock.close()

# 如果检测到用户输入敲击键盘,那么就退出

if not running:

break

server.close()

2.4 epoll版服务器实现

为了解决select版并发连接数目的限制,出现了poll版,与select版几乎相同,唯一不同的是数量不受限制,仍是用的轮询方式。后来为了解决poll版轮询监测方式低下的问题出现了epoll版,epoll版相当于“有问题举手”,而不是“挨个问是否有问题”。

epoll版的优点:

1. 没有最?并发连接的限制, 能打开的FD(指的是?件描述符), 通俗的理解就是套接字对应的数字编号)的上限远?于1024

2. 效率提升, 不是轮询的?式, 不会随着FD数?的增加效率下降。 只有活跃可?的FD才会调?callback函数; 即epoll最?的优点就在于它只管你“活跃”的连接, ?跟连接总数?关, 因此在实际的?络环境中, epoll的效率就会远远?于select和poll。

epoll版tcp服务器代码如下:

代码解释:

epoll的三种事件:

EPOLLIN (可读)

EPOLLOUT (可写)

EPOLLET (ET模式)

epoll对?件描述符的操作有两种模式: LT(level trigger 水平触发) 和ET(edge trigger 边沿触发) 。 LT模式是默认模式, LT模式与ET模式的区别如下:

LT模式: 当epoll检测到描述符事件发?并将此事件通知应?程序, 应?程序可以不?即处理该事件

ET模式: 当epoll检测到描述符事件发?并将此事件通知应?程序, 应?程序必须?即处理该事件,否则会丢失

import socket

import select

# 创建套接字

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# 设置可以重复使?绑定的信息

s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

# 绑定本机信息

s.bind(("",7788))

# 变为被动

s.listen(10)

# 创建?个epoll对象

epoll=select.epoll()

# 测试, ?来打印套接字对应的?件描述符

# print s.fileno()

# print select.EPOLLIN|select.EPOLLET

# 注册事件到epoll中

# epoll.register(fd, [eventmask])

# 注意, 如果fd已经注册过, 则会发?异常

# 将创建的socket添加到epoll的事件监听中

# [eventmask]为监听的事件列表,有列表中的事件时才会放入epoll列表,

# 事件有三种,EPOLLIN接收数据事件,EPOLLOUT发送数据,EPOLLET模式(水平触发或边沿触发)

epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET)

# connections用于存储socket,addresses用于存储端口,

# 它们都为字典,key为socket的文件描述符,value为socket或端口!

connections = {}

addresses = {}

# 循环等待客户端的到来或者对?发送数据

while True:

# epoll 进? fd 扫描的地? -- 未指定超时时间则为阻塞等待

# 等价于select版本中的 readable,xxx,yyy = select([],[],[])

# 不为轮询,使用的是事件通知机制,为本代码的核心

epoll_list=epoll.poll()

# 对事件进?判断

for fd,events in epoll_list:

# print fd

# print events

# 如果是socket创建的套接字被激活

if fd == s.fileno():

conn,addr=s.accept()

print('有新的客户端到来%s'%str(addr))

# 将 conn 和 addr 信息分别保存起来

# 注意connections和address为字典,以key、value存储

connections[conn.fileno()] = conn

addresses[conn.fileno()] = addr

# 向 epoll 中注册 连接 socket 的 可读 事件

epoll.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)

elif events == select.EPOLLIN:

# 从激活 fd 上接收

recvData = connections[fd].recv(1024)

if len(recvData)>0:

print('recv:%s'%recvData)

else:

# 从 epoll 中移除该 连接 fd

epoll.unregister(fd)

# server 则主动关闭该 连接 fd

connections[fd].close()

print("%s---offline---"%str(addresses[fd]))

三、多进程服务器和多线程服务器

代码说明:

1、多进程实现和多线程实现几乎相同,不同点:1、创建的时候;2、while中多进程实现中,由于子进程复制了一份,所以可以关闭,多线程中,子线程之间共享资源,所以在while中不能关闭。

2、代码中使用try...finally,目的是可以使用Ctrl+C强制结束进程或线程。

多进程服务器代码如下:

#coding=utf-8

from socket import *

from multiprocessing import *

from time import sleep

# 处理客户端的请求并为其服务

def dealWithClient(newSocket,destAddr):

while True:

recvData = newSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(destAddr), recvData))

else:

print('[%s]客户端已经关闭'%str(destAddr))

break

newSocket.close()

def main():

serSocket = socket(AF_INET, SOCK_STREAM)

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

serSocket.listen(5)

try:

while True:

print('-----主进程,,等待新客户端的到来------')

newSocket,destAddr = serSocket.accept()

print('-----主进程,,接下来创建?个新的进程负责数据处理------')

client = Process(target=dealWithClient, args=(newSocket,destAddr))

client.start()

#因为已经向?进程中copy了?份(引?),并且?进程中这个套接字所以关闭

newSocket.close()

finally:

#当为所有的客户端服务完之后再进?关闭,表示不再接收新的客户端的链接

serSocket.close()

if __name__ == '__main__':

main()

多线程服务器代码如下:

#coding=utf-8

from socket import *

from threading import Thread

from time import sleep

# 处理客户端的请求并执?事情

def dealWithClient(newSocket,destAddr):

while True:

recvData = newSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(destAddr), recvData))

else:

print('[%s]客户端已经关闭'%str(destAddr))

break

newSocket.close()

def main():

serSocket = socket(AF_INET, SOCK_STREAM)

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

serSocket.listen(5)

try:

while True:

print('-----主进程, , 等待新客户端的到来------')

newSocket,destAddr = serSocket.accept()

print('-----主进程, , 接下来创建?个新的进程负责数据处理[%s]-----')

client = Thread(target=dealWithClient, args=(newSocket,destAddr))

client.start()

#因为线程中共享这个套接字, 如果关闭了会导致这个套接字不可?,

#但是此时在线程中这个套接字可能还在收数据, 因此不能关闭

#newSocket.close()

finally:

serSocket.close()

if __name__ == '__main__':

main()

四、协程

协程学习资后台私信【协程】获取文档代码

进程里面有线程,线程里面有协程。协程不牵扯到切换,并且能完成多任务。

注意:计算密集型时用多进程;IO密集型使用多线程、多协程。

通俗的理解: 在?个线程中的某个函数, 可以在任何地?保存当前函数的?些临时变量等信息, 然后切换到另外?个函数中执?, 注意不是通过调?函数的?式做到的, 并且切换的次数以及什么时候再切换到原来的函数都由开发者??确定。

协程和线程的区别:线程切换从系统层?远不?保存和恢复 CPU上下?这么简单。 操作系统为了程序运营的?效性每个线程都有??缓存Cache等等数据, 操作系统还会帮你做这些数据的恢复操作。所以线程的切换?常耗性能。 但是协程的切换只是单纯的操作CPU的上下?, 所以?秒钟切换个上百万次系统都抗的住。

协程调度:操作系统不感知协程,所以操作系统不会对协程调度。 ?前的协程框架?般都是设计成 1:N 模式。 所谓 1:N 就是?个线程作为?个容器??放置多个协程。 那么谁来适时的切换这些协程? 答案是有协程自己主动让出CPU, 也就是每个协程池??有?个调度器, 这个调度器是被动调度的。 意思就是他不会主动调度。 ?且当?个协程发现自己执行不下去了(比如异步等待?络的数据回来, 但是当前还没有数据到), 这个时候就可以由这个协程通知调度器, 这个时候执?到调度器的代码, 调度器根据事先设计好的调度算法找到当前最需要CPU的协程。 切换这个协程的CPU上下?把CPU的运?权交给这个协程, 直到这个协程出现执行不下去需要等等的情况, 或

者它调?主动让出CPU的API之类, 触发下?次调度。

协程调度存在问题:假设一个线程中有?个协程是CPU密集型的他没有IO操作, 也就是自己不会主动触发调度器调度的过程, 那么就会出现其他协程得不到执?的情况, 所以这种情况下需要程序员? ?避免。 这是?个问题, 假设业务开发的?员并不懂这个原理的话就可能会出现问题。

协程的优点:在IO密集型的程序中由于IO操作远远慢于CPU的操作, 所以往往需要CPU去等IO操作。 同步IO下系统需要切换线程, 让操作系统可以在IO过程中执?其他的东?。 这样虽然代码是符合?类的思维习惯但是由于?量的线程切换带来了?量的性能的浪费, 尤其是IO密集型的程序。

所以?们发明了异步IO。 就是当数据到达的时候触发我的回调。 来减少线程切换带来性能损失。 但是这样的坏处也是很?的, 主要的坏处就是操作被“分片” 了, 代码写的不是 “一气呵成” 这种。 而是每次来段数据就要判断 数据够不够处理, 够处理就处理, 不够处理就再等等。 这样代码的可读性很低, 其实也不符合?类的习惯。

但是协程可以很好解决这个问题。 比如 把?个IO操作 写成?个协程。 当触发IO操作的时候就自动让出CPU给其他协程。 要知道协程的切换很轻的。 协程通过这种对异步IO的封装 既保留了性能也保证了代码的容易编写和可读性。在?IO密集型的程序下很好。 但是高CPU密集型的程序下没啥好处。

4.1 协程的生成器实现

协程使用生成器来实现的,代码如下(只切换了函数调用,所以效率比较高):

import time

def A():

while True:

print("----A---")

yield

time.sleep(0.5)

def B(c):

while True:

print("----B---")

c.next()

time.sleep(0.5)

if __name__=='__main__':

a = A()

B(a)

结果如下:

--B--

--A--

--B--

--A--

--B--

--A--

--B--

--A--

...省略...

4.2 协程的greenlet实现

与生成器实现类似。

注意:进程、线程的调用是操作系统决定的,执行顺序不可预测,而协程是程序员决定的执行顺序可预测,这由以下代码可知(当执行到xx.switch()时会切换)。

使用下面命令安装greenlet:

sudo pip install greenlet #python2的安装方式

sudo pip3 install greenlet #python3的安装方式

协程的greenlet实现代码如下:

#coding=utf-8

from greenlet import greenlet

import time

def test1():

while True:

print "---A--"

gr2.switch() # 切换到gr2(即test2)中执行,test2执行切换时会从当前接着执行

time.sleep(0.5)

def test2():

while True:

print "---B--"

gr1.switch() # 切换到gr1(即test1)中执行,test1执行切换时会从当前接着执行

time.sleep(0.5)

gr1 = greenlet(test1)

gr2 = greenlet(test2)

#切换到gr1(即test1函数)中执行

gr1.switch()

结果如下:

--A--

--B--

--A--

--B--

--A--

--B--

--A--

...省略...

4.3 协程的gevent实现

gevent是对greenlet的再次封装,不用程序员自己编程切换,当遇到需要切换的地方会自动切换。

4.3.1 gevent的使用

#coding=utf-8

#请使?python 2 来执?此程序

import gevent

def f(n):

for i in range(n):

print gevent.getcurrent(), i

g1 = gevent.spawn(f, 5) # 绑定f函数,执行5次

g2 = gevent.spawn(f, 5)

g3 = gevent.spawn(f, 5)

# 清除协程

g1.join()

g2.join()

g3.join()

执行结果:

一瞬间执行完毕,g1、g2、g3依次顺序执行,并非交替执行,不是我们想要的结果。

<Greenlet at 0x10e49f550: f(5)> 0

<Greenlet at 0x10e49f550: f(5)> 1

<Greenlet at 0x10e49f550: f(5)> 2

<Greenlet at 0x10e49f550: f(5)> 3

<Greenlet at 0x10e49f550: f(5)> 4

<Greenlet at 0x10e49f910: f(5)> 0

<Greenlet at 0x10e49f910: f(5)> 1

<Greenlet at 0x10e49f910: f(5)> 2

<Greenlet at 0x10e49f910: f(5)> 3

<Greenlet at 0x10e49f910: f(5)> 4

<Greenlet at 0x10e49f4b0: f(5)> 0

<Greenlet at 0x10e49f4b0: f(5)> 1

<Greenlet at 0x10e49f4b0: f(5)> 2

<Greenlet at 0x10e49f4b0: f(5)> 3

<Greenlet at 0x10e49f4b0: f(5)> 4

4.3.2 gevent的切换执行

上面顺序执行的原因是在f函数中没有调用延时,所以不会切换。gevent当遇到耗时操作时才会切换,所以增加一个延时函数使它能够切换,代码如下:

import gevent

def f(n):

for i in range(n):

print gevent.getcurrent(), i

#?来模拟?个耗时操作, 注意不是time模块中的sleep

gevent.sleep(1)

g1 = gevent.spawn(f, 5)

g2 = gevent.spawn(f, 5)

g3 = gevent.spawn(f, 5)

g1.join()

g2.join()

g3.join()

结果如下:

<Greenlet at 0x7fa70ffa1c30: f(5)> 0

<Greenlet at 0x7fa70ffa1870: f(5)> 0

<Greenlet at 0x7fa70ffa1eb0: f(5)> 0

<Greenlet at 0x7fa70ffa1c30: f(5)> 1

<Greenlet at 0x7fa70ffa1870: f(5)> 1

<Greenlet at 0x7fa70ffa1eb0: f(5)> 1

<Greenlet at 0x7fa70ffa1c30: f(5)> 2

<Greenlet at 0x7fa70ffa1870: f(5)> 2

<Greenlet at 0x7fa70ffa1eb0: f(5)> 2

<Greenlet at 0x7fa70ffa1c30: f(5)> 3

<Greenlet at 0x7fa70ffa1870: f(5)> 3

<Greenlet at 0x7fa70ffa1eb0: f(5)> 3

<Greenlet at 0x7fa70ffa1c30: f(5)> 4

<Greenlet at 0x7fa70ffa1870: f(5)> 4

<Greenlet at 0x7fa70ffa1eb0: f(5)> 4

4.3.3 gevent的服务器实现

注意:要使用gevent实现服务器,不能使用默认的socket,而是使用gevent自己的socket,gevent将常用的耗时操作都重写了一遍,用于检测是否为耗时操作。

import sys

import time

import gevent

from gevent import socket,monkey

# 此语句会将本代码改写,位于编译器级的,具体不清楚!(python为动态语言在执行中可以修改)

# 必须使用!!!

monkey.patch_all()

def handle_request(conn):

while True:

#--------------#1处#-----------------

data = conn.recv(1024) # 这是gevent中的recv,为耗时操作,会切换到2处!

if not data:

conn.close()

break

print("recv:", data)

conn.send(data)

def server(port):

s = socket.socket()

s.bind(('', port))

s.listen(5)

while True:

#--------------#2处#-----------------

cli, addr = s.accept() # 这是gevent中的accept,为耗时操作,会进行切换!

# 注意:第一次到这里时只有一个协程,不需要切换,在此等待!

# 不为第一次时切换到1处!

gevent.spawn(handle_request, cli)

if __name__ == '__main__':

server(7788)

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: