├── README.md ├── db数据库.py ├── qrcode.jpg ├── 知网文章下载器_一体.exe └── 远程文件传输器(知网版).py /README.md: -------------------------------------------------------------------------------- 1 | # RemoteFileTransfer 2 | 远程文件传输程序,以知网为例 3 | . 4 | . 原贴:https://www.52pojie.cn/thread-1015065-1-1.html 5 | ## 原理 6 | 1. **服务端**运行在校园网环境,**客户端**为用户使用。双方建立连接后,客户端上报知网文章URL,服务端获取文章后,即可下发到客户端。 7 | 2. 搜索下载顺序为:pdf下载 -> caj下载 -> 整本下载 -> 异常。 8 | 3. 引入排队下载机制,当有多个用户同时请求下载时,将按照先进先出的原则依次运行。 9 | 4. 同时使用sqlite3数据库对用户进行管理。 10 | 5. 增强容错能力,当正在下载的用户中途离线,也不影响程序继续运行。 11 | 6. 降低CPU占用。 12 | 7. 增加设置120s传输超时。 13 | 8. 为了防止大家各自的程序混乱,所以在打开软件后会要求你输入一个“序列号”,随便输,只要客户端与服务端对应就能联通了。 14 | 9. 如果大家没有校园网,可以跟我联系,我适当运行一下服务端,同时为了和之前的兼容,我的序列号就设置为空了,即直接回车。 15 | 16 | ## 拓展 17 | 1. 程序运行后可选择服务端或是客户端(但**知网文章下载.exe**固定为了**客户端**),也就是说,只要稍加改动源码,就可以将程序DIY为两台PC之间的远程文件传输。如: 18 | a. 办公室电脑运行**服务端**, 家里电脑运行**客户端**,即可远程利用公司内网下载权限文件至处于外网环境下的电脑。 19 | b. 服务器器运行**服务端**,自己电脑运行**客户端**,即可将服务器上文件下载下来。 20 | 21 | 下载完成说明: 22 | 出现 “完成,重启软件排队” 23 | 24 | 程序中用到的MQTT服务器,大家可以继续用我的,只不过买的服务器还有一个月就要过期了。 25 | 若右键无法粘贴,在标题那里右击,选属性;然后把“快速编辑模式”打钩,这样就能右键粘贴输入了。 26 | 27 | 因为担心有不良用心的人牟利,且防止滥用资源,所以一个人初始给了5次使用次数。用完了可以使用**db数据库.py**修改数据库。 28 | . 29 | . 30 | . 31 | * 演示视频如下 32 | http://ncurobot.club/Share/【视频演示】知网文章下载器.wmv 33 | 34 | * 体验链接:https://pan.baidu.com/s/1f6XY7dNNdX0ZH-OIMDMVpQ 提取码:lfku 35 | 36 | 公众号: 37 | ![公众号](https://github.com/1061700625/RemoteFileTransfer/blob/master/qrcode.jpg) 38 | -------------------------------------------------------------------------------- /db数据库.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import sqlite3 4 | 5 | class DB: 6 | def __init__(self): 7 | self.id = 0 8 | self.Start() 9 | self.CreatTable() 10 | self.id = len(self.SelectALL()) 11 | # print(self.id) 12 | 13 | def Start(self): 14 | self.conn = sqlite3.connect(r'C:\Users\SXF\Desktop\dist\ZW.db') 15 | self.cursor = self.conn.cursor() 16 | 17 | def CreatTable(self): 18 | try: 19 | self.cursor.execute('''CREATE TABLE ZhiWang( 20 | ID INT PRIMARY KEY NOT NULL, 21 | IP VARCHAR(30) NOT NULL , 22 | CNT INT NOT NULL);''') 23 | return 1 24 | except Exception as e: 25 | print(e) 26 | return 0 27 | 28 | def Insert(self, IP): 29 | try: 30 | self.id += 1 31 | self.cursor.execute('''INSERT INTO ZhiWang (ID, IP, CNT) VALUES (?, ?,5)''', (self.id, IP)) 32 | self.conn.commit() 33 | return 1 34 | except Exception as e: 35 | print(e) 36 | return 0 37 | 38 | def Select(self, IP): 39 | self.cursor.execute('''SELECT * from ZhiWang WHERE IP=(?)''', (IP,)) 40 | return self.cursor.fetchall() 41 | 42 | def UpdateDown(self, IP): 43 | cnt = self.cursor.execute("SELECT * from ZhiWang WHERE IP=(?);", (IP,)).fetchone()[2] 44 | self.cursor.execute("UPDATE ZhiWang set CNT = (?) WHERE IP=(?);", (cnt - 1, IP)) 45 | self.conn.commit() 46 | 47 | def Check(self, IP, cnt): 48 | self.cursor.execute('''SELECT * from ZhiWang WHERE IP=(?)''', (IP,)) 49 | if self.cursor.fetchone()[2] >= cnt: 50 | return 1 51 | else: 52 | return 0 53 | 54 | def Close(self): 55 | self.cursor.close() 56 | self.conn.close() 57 | 58 | def SelectALL(self): 59 | sql = "SELECT * from ZhiWang;" 60 | self.cursor.execute(sql) 61 | return self.cursor.fetchall() 62 | 63 | def UpdateAdd(self, IP, count): 64 | cnt = self.cursor.execute("SELECT * from ZhiWang WHERE IP=(?);", (IP,)).fetchone()[2] 65 | self.cursor.execute("UPDATE ZhiWang set CNT = (?) WHERE IP=(?);", (count, IP)) 66 | self.conn.commit() 67 | 68 | def AlterAdd(self): 69 | self.cursor.execute('''ALTER TABLE ZhiWang ADD LOC VARCHAR(100) NULL DEFAULT '';''') 70 | self.conn.commit() 71 | 72 | 73 | def UpdateFilename(self, Filename, IP): 74 | try: 75 | temp = self.cursor.execute('''SELECT FILENAME from ZhiWang WHERE IP=(?)''', (IP,)).fetchone()[0] 76 | temp = temp + ';' + Filename 77 | self.cursor.execute('''UPDATE ZhiWang set FILENAME = (?) WHERE IP=(?);''', (Filename, IP)) 78 | self.conn.commit() 79 | return 1 80 | except Exception as e: 81 | print(e) 82 | return 0 83 | 84 | def AddIPsCNT(IP, CNT): # 为某个IP增加次数 85 | db = DB() 86 | db.UpdateAdd(IP, CNT) 87 | res = db.Select(IP) 88 | db.Close() 89 | print(res) 90 | 91 | def SearchAll(): 92 | db = DB() 93 | res = db.SelectALL() 94 | db.Close() 95 | print(res) 96 | 97 | def Insert(ID, IP, CNT): 98 | pass 99 | 100 | 101 | 102 | AddIPsCNT('112.244.79.95', 10) 103 | # SearchAll() 104 | -------------------------------------------------------------------------------- /qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1061700625/RemoteFileTransfer/9ec1676c915ddc3269bcc93495543afd0ae77f01/qrcode.jpg -------------------------------------------------------------------------------- /知网文章下载器_一体.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1061700625/RemoteFileTransfer/9ec1676c915ddc3269bcc93495543afd0ae77f01/知网文章下载器_一体.exe -------------------------------------------------------------------------------- /远程文件传输器(知网版).py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import re 3 | import sqlite3 4 | import requests 5 | import time 6 | import paho.mqtt.client as mqtt 7 | from bs4 import BeautifulSoup 8 | import os 9 | import threading 10 | import ctypes 11 | import inspect 12 | import sys 13 | from goto import with_goto 14 | 15 | MQTTHOST = "ncurobot.club" 16 | MQTTPORT = 1883 17 | USERNAME = "SXF" 18 | PASSWORD = "1061700625" 19 | HEARTBEAT = 60 20 | 21 | class DB: 22 | def __init__(self): 23 | self.id = 0 24 | self.Start(DBPATH) 25 | self.CreatTable() 26 | self.id = len(self.SelectALL()) 27 | self.Close() 28 | 29 | def Start(self, database): 30 | self.conn = sqlite3.connect(database) 31 | self.cursor = self.conn.cursor() 32 | 33 | def CreatTable(self): 34 | try: 35 | self.cursor.execute('''CREATE TABLE ZhiWang( 36 | ID INT PRIMARY KEY NOT NULL, 37 | IP VARCHAR(30) NOT NULL , 38 | CNT INT NOT NULL, 39 | FILENAME VARCHAR(100) NULL DEFAULT ' ', 40 | LOC varchar(100) NULL DEFAULT ' ' 41 | );''') 42 | return 1 43 | except Exception as e: 44 | # print(e) 45 | return 0 46 | 47 | def Insert(self, IP): 48 | try: 49 | self.id += 1 50 | self.cursor.execute('''INSERT INTO ZhiWang (ID, IP, CNT, FILENAME, LOC) VALUES (?, ?,5,' ', ' ')''', (self.id, IP)) 51 | self.conn.commit() 52 | return 1 53 | except Exception as e: 54 | print("!!! Insert:", str(e)) 55 | return 0 56 | 57 | def UpdateLoc(self, IP, Loc): 58 | try: 59 | self.cursor.execute('''UPDATE ZhiWang set LOC=(?) WHERE IP=(?)''', (Loc, IP)) 60 | self.conn.commit() 61 | return 1 62 | except Exception as e: 63 | print(e) 64 | return 0 65 | 66 | def UpdateFilename(self, Filename, IP): 67 | try: 68 | temp = self.cursor.execute('''SELECT FILENAME from ZhiWang WHERE IP=(?)''', (IP,)).fetchone()[0] 69 | temp = temp + ';' + Filename 70 | self.cursor.execute('''UPDATE ZhiWang set FILENAME = (?) WHERE IP=(?);''', (temp, IP)) 71 | self.conn.commit() 72 | return 1 73 | except Exception as e: 74 | print('!!! UpdateFilename: ' + str(e)) 75 | return 0 76 | 77 | def Select(self, IP): 78 | try: 79 | self.cursor.execute("SELECT * from ZhiWang WHERE IP=(?)", (IP,)) 80 | except Exception as e: 81 | print("!!! Select:", str(e)) 82 | pass 83 | return self.cursor.fetchall() 84 | 85 | def UpdateDown(self, IP): 86 | cnt = self.cursor.execute("SELECT * from ZhiWang WHERE IP=(?);", (IP,)).fetchone()[2] 87 | self.cursor.execute("UPDATE ZhiWang set CNT = (?) WHERE IP=(?);", (cnt - 1, IP)) 88 | self.conn.commit() 89 | 90 | def UpdateUp(self, IP): 91 | cnt = self.cursor.execute("SELECT * from ZhiWang WHERE IP=(?);", (IP,)).fetchone()[2] 92 | self.cursor.execute("UPDATE ZhiWang set CNT = (?) WHERE IP=(?);", (cnt + 1, IP)) 93 | self.conn.commit() 94 | 95 | def UpdateAdd(self, IP): 96 | cnt = self.cursor.execute("SELECT * from ZhiWang WHERE IP=(?);", (IP,)).fetchone()[2] 97 | self.cursor.execute("UPDATE ZhiWang set CNT = (?) WHERE IP=(?);", (5, IP)) 98 | self.conn.commit() 99 | 100 | def Check(self, IP, cnt): 101 | self.cursor.execute('''SELECT * from ZhiWang WHERE IP=(?)''', (IP,)) 102 | if self.cursor.fetchone()[2] >= cnt: 103 | return 1 104 | else: 105 | return 0 106 | 107 | def Close(self): 108 | self.cursor.close() 109 | self.conn.close() 110 | 111 | def SelectALL(self): 112 | sql = "SELECT * from ZhiWang;" 113 | self.cursor.execute(sql) 114 | return self.cursor.fetchall() 115 | 116 | HEAD = b'#W' 117 | DATAEND1 = b'@#$%' 118 | DATAEND2 = b'%$#@' 119 | REC_FLAG = 0 120 | C_CONNECT = 0 121 | C_ALLOW_URL = 0 122 | class MQTT: 123 | def __init__(self): 124 | self.firstenter = 1 125 | self.name = '1.txt' 126 | self.filesize = 0 127 | self.p_last = 0 128 | self.first_frame = 1 129 | self.topic_s = '/serverListen/'+PRISEQ 130 | self.topic_c = '/clientListen/'+PRISEQ 131 | self.TOPICBUFF = [] 132 | self.URLBUFF = [] 133 | self.topicNow = '' 134 | self.urlNow = '' 135 | if S_OR_C == 'S': 136 | self.db = DB() 137 | self.time_pre = 0 138 | self.time_now = 0 139 | self.THREADBUFF = [] 140 | self.dwnTime_pre = 0 141 | self.dwnTime_now = 0 142 | 143 | def GetCAJ(self, url): 144 | print(">> 请求文件") 145 | try: 146 | sess = requests.session() 147 | requests.adapters.DEFAULT_RETRIES = 5 148 | sess.keep_alive = False 149 | html = sess.get(url, timeout=30) 150 | soup = BeautifulSoup(html.text, 'lxml') 151 | suffix = '.pdf' 152 | href = '' 153 | if soup.find(id='pdfDown'): 154 | suffix = '.pdf' 155 | href = soup.find(id='pdfDown') 156 | elif soup.find(id='cajDown'): 157 | suffix = '.caj' 158 | href = soup.find(id='cajDown') 159 | elif soup.find(class_='icon-dlGreen'): 160 | suffix = '.caj' 161 | href = soup.find(class_='icon-dlGreen') 162 | else: 163 | href = '' 164 | try: 165 | href = href['href'] 166 | except Exception as e: 167 | print("!!! GetCAJ_1:", str(e)) 168 | print('url: ', url) 169 | print('href: ', href) 170 | client.publish(self.topicNow, ">> MSG: 下载失败, 这篇文章提供'pdf/caj/整本'下载吗?", 1) 171 | return None 172 | 173 | time.sleep(1) 174 | href = r'https://kns.cnki.net' + href 175 | name = soup.find(class_='wxTitle').find(class_='title').string + suffix 176 | print(">> 文件名称:", name) 177 | print(">> 文件链接:", href) 178 | files = sess.get(href, timeout=30).content 179 | # if 'alert' in files.decode('utf-8'): 180 | # client.publish(self.topicNow, ">> MSG: \r\n%s" % files.decode('utf-8'), 1) 181 | # print(files.decode('utf-8')) 182 | # return None 183 | with open(name, 'wb+') as f: 184 | f.write(files) 185 | print(">> 下载完成") 186 | return name 187 | except Exception as e: 188 | print("!!! GetCAJ_2:", str(e)) 189 | return None 190 | 191 | def process_data(self, contents, mode=0): # mode=0:数据帧;mode=1:结束帧 192 | if mode == 0: 193 | return HEAD + contents + DATAEND1 194 | if mode == 1: 195 | return HEAD + contents + DATAEND2 196 | 197 | def send_data(self, strings, topic): 198 | # try: 199 | # client.publish(topic=self.topic_c, payload=strings.encode('utf8'), qos=0) 200 | # except: 201 | client.publish(topic=topic, payload=strings, qos=1) 202 | 203 | # 打开文件 204 | def open_file(self, file_path): 205 | try: 206 | self.file = open(file_path, 'rb') 207 | self.first_frame = 1 208 | except Exception as e: 209 | self.file.close() 210 | print(e) 211 | 212 | # 读取指定字节文件 213 | def read_file(self, bytes_cnt, mode=0): 214 | try: 215 | read_content = '' 216 | if mode == 0: # 读一次 217 | read_content = self.file.read() 218 | elif mode == 1: # 读指定字节 219 | read_content = self.file.read(bytes_cnt) 220 | return read_content, self.file.tell() 221 | except Exception as e: 222 | self.file.close() 223 | print(e) 224 | 225 | # 发送文件 226 | def send_file(self, path, topic): 227 | global REC_FLAG, C_ALLOW_URL, C_NOT_END 228 | self.open_file(path) 229 | time_start = time.time() 230 | total_size = os.path.getsize(path) 231 | while True: 232 | if self.first_frame == 1: 233 | self.first_frame = 0 234 | file_name = path.split('\\')[-1]+';'+str(total_size) 235 | print('>> 文件名:', file_name) 236 | send_filedata = self.process_data(file_name.encode(), 0) 237 | # print(send_filedata) 238 | self.send_data(send_filedata, topic) 239 | # time.sleep(0.05) 240 | 241 | read_content, p = self.read_file(bytes_cnt=3072, mode=1) 242 | print('>> 当前指针:{}/{}'.format(p, total_size)) 243 | # if p - self.p_last < 4096: 244 | if p >= total_size: 245 | send_filedata = self.process_data(read_content, 1) 246 | self.send_data(send_filedata, topic) 247 | self.file.close() 248 | self.first_frame = 0 249 | # print(send_filedata) 250 | print('>> File End!') 251 | print(">> 耗时(s):%.2f" % (time.time()-time_start)) 252 | self.p_last = 0 253 | C_NOT_END = 0 254 | C_ALLOW_URL = 0 255 | self.THREADBUFF.pop() 256 | os.remove(path) 257 | break 258 | else: 259 | self.p_last = p 260 | send_filedata = self.process_data(read_content, 0) 261 | self.send_data(send_filedata, topic) 262 | while REC_FLAG == 0: 263 | pass 264 | REC_FLAG = 0 265 | # time.sleep(0.05) 266 | 267 | # MQTT连接回调 268 | def on_connect(self, client, userdata, flags, rc): 269 | if rc == 0: 270 | print(">> 连接云服务器成功") 271 | if S_OR_C == 'S': 272 | print(">> 订阅主题 -> %s" % self.topic_s+'#') 273 | client.subscribe(self.topic_s+'#') 274 | elif S_OR_C == 'C': 275 | print(">> 你的IP -> %s" % self.topic_c.split('/')[-1]) 276 | client.subscribe(self.topic_c) 277 | 278 | # 各状态初始化 279 | def paras_Init(self): 280 | global REC_FLAG, C_ALLOW_URL, C_NOT_END 281 | self.stop_thread(self.THREADBUFF.pop()) 282 | self.p_last = 0 283 | C_NOT_END = 0 284 | C_ALLOW_URL = 0 285 | REC_FLAG = 0 286 | self.file.close() 287 | self.first_frame = 0 288 | 289 | # MQTT接收回调 290 | def on_message(self, client, userdata, msg): 291 | global C_NOT_END, REC_FLAG, C_CONNECT, C_ALLOW_URL, REMIND_MSG 292 | if S_OR_C == 'C': # 当前客户端 293 | MQTT_Rx_Buff = msg.payload 294 | end_flag = 0 295 | if MQTT_Rx_Buff.startswith(b'>> CONNECTED'): 296 | print(">> 连接[服务端]成功!") 297 | C_CONNECT = 1 298 | return 299 | elif MQTT_Rx_Buff.startswith(b'>> MSG'): 300 | print(MQTT_Rx_Buff.decode('utf-8')) 301 | return 302 | 303 | if MQTT_Rx_Buff.endswith(DATAEND2): 304 | end_flag = 1 305 | MQTT_Rx_Buff = MQTT_Rx_Buff[2:-4] 306 | if self.firstenter == 1: 307 | self.firstenter = 0 308 | print('>> 第一帧') 309 | self.name = MQTT_Rx_Buff.decode('utf-8').split(';')[0] 310 | self.filesize = MQTT_Rx_Buff.decode('utf-8').split(';')[1] 311 | if os.path.isfile(self.name): 312 | os.remove(self.name) 313 | client.publish(self.topic_s, 'P', 1) 314 | else: 315 | with open(self.name, 'ab+') as fp: 316 | fp.write(MQTT_Rx_Buff) 317 | print('>> 数据帧[%s/%s]' % (fp.tell(), self.filesize)) 318 | client.publish(self.topic_s, 'P', 1) 319 | if end_flag == 1: 320 | print(">> 完成,重启软件排队") 321 | self.firstenter = 1 322 | end_flag = 0 323 | 324 | elif S_OR_C == 'S': # 当前服务端 325 | MQTT_Rx_Buff = str(msg.payload, encoding='utf-8') 326 | IP = msg.topic.split('/')[-1] 327 | temptopic = '/clientListen/' + PRISEQ + msg.topic.split('/')[-1] 328 | if MQTT_Rx_Buff.startswith(">> DEAD"): 329 | print(">> [%s] 客户端下线: %s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), temptopic)) 330 | if self.topicNow.split('/')[-1] == IP and C_ALLOW_URL == 1: # 正在下载的客户端中途下线 331 | print(">> 正在下载的客户端中途下线") 332 | self.paras_Init() 333 | return 334 | self.URLBUFF.pop(self.TOPICBUFF.index(temptopic)) 335 | self.TOPICBUFF.remove(temptopic) 336 | return 337 | elif ('/clientListen/'+PRISEQ) in MQTT_Rx_Buff: 338 | print(">> [%s] 客户端上线: %s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), temptopic)) 339 | client.publish(temptopic, REMIND_MSG, qos=1) 340 | 341 | self.db.Start(DBPATH) 342 | if not self.db.Select(IP): # 不在表内 343 | print(">> 新增入表 - ", IP) 344 | self.db.Insert(IP) 345 | self.db.Close() 346 | 347 | time.sleep(1) 348 | client.publish(temptopic, '>> CONNECTED', 1) 349 | return 350 | 351 | elif MQTT_Rx_Buff.startswith('http'): 352 | self.db.Start(DBPATH) 353 | print(">> 收到下载请求 -", IP) 354 | if not self.db.Select(IP): # 不在表内 355 | print(">> 新增入表 -", IP) 356 | self.db.Insert(IP) 357 | if self.db.Check(IP, 0) == 0: # 次数用完 358 | client.publish(temptopic, '>> MSG:你当前次数已用完,免费增加请与我联系', 1) 359 | print(">> 次数用完 -", IP) 360 | return 361 | remaincnt = self.db.Select(IP)[0][2] 362 | print(">> 次数充足 - %s [%s]" % (remaincnt, IP)) 363 | try: 364 | self.TOPICBUFF.append(temptopic) 365 | client.publish(temptopic, '>> MSG:你当前队伍[%s/%s],请等待,会自动开始。可用次数[%s]' % (self.TOPICBUFF.index(temptopic)+1, len(self.TOPICBUFF), remaincnt), 1) 366 | self.URLBUFF.append(MQTT_Rx_Buff) 367 | self.db.UpdateDown(IP) 368 | self.db.Close() 369 | except Exception as e: 370 | print(e) 371 | self.db.Close() 372 | return 373 | 374 | if MQTT_Rx_Buff == 'P': # 传完一帧等待客户端回应 375 | self.dwnTime_now = time.time() 376 | REC_FLAG = 1 377 | return 378 | 379 | def CreatSendTask(self): 380 | global C_ALLOW_URL, C_NOT_END 381 | while True: 382 | if C_ALLOW_URL == 0: # 允许发一次文件 383 | if len(self.TOPICBUFF) > 0 and len(self.URLBUFF) > 0: 384 | self.topicNow = self.TOPICBUFF.pop(0) 385 | self.urlNow = self.URLBUFF.pop(0) 386 | name = self.GetCAJ(self.urlNow) 387 | if name: 388 | self.db.Start(DBPATH) 389 | self.db.UpdateFilename(name, self.topicNow.split('/')[-1]) 390 | self.db.Close() 391 | print(">> 开始发送") 392 | client.publish(topic=self.topicNow, payload=">> MSG: 获取文章成功", qos=1) 393 | try: 394 | send_task = threading.Thread(target=self.send_file, args=(name, self.topicNow)) 395 | send_task.setDaemon(True) 396 | send_task.start() 397 | self.THREADBUFF.append(send_task) 398 | self.dwnTime_now = time.time() 399 | C_ALLOW_URL = 1 400 | except Exception as e: 401 | print(e) 402 | # send_task.join() 403 | else: 404 | print(">> 下载失败") 405 | self.db.Start(DBPATH) 406 | self.db.UpdateUp(self.topicNow.split('/')[-1]) 407 | self.db.Close() 408 | client.publish(topic=self.topicNow, payload=">> MSG:下载失败,次数不减; 检查URL是否正确/知网页面是否支持pdf文档,重启软件排队。若还是不行,那就是不支持下载了", qos=1) 409 | else: 410 | if int(time.time() - self.dwnTime_now) > 120: 411 | print(">> 传输超时") 412 | client.publish(topic=self.topicNow, payload=">> MSG:网速太慢,传送超时,已终止连接", qos=1) 413 | self.paras_Init() 414 | time.sleep(2) 415 | 416 | ################################强制关闭线程################################################## 417 | def _async_raise(self, tid, exctype): 418 | """raises the exception, performs cleanup if needed""" 419 | tid = ctypes.c_long(tid) 420 | if not inspect.isclass(exctype): 421 | exctype = type(exctype) 422 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) 423 | if res == 0: 424 | raise ValueError("invalid thread id") 425 | elif res != 1: 426 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) 427 | raise SystemError("PyThreadState_SetAsyncExc failed") 428 | 429 | def stop_thread(self, thread): 430 | self._async_raise(thread.ident, SystemExit) 431 | ############################################################################################### 432 | 433 | def mqtt(self): 434 | if S_OR_C == 'C': 435 | self.topic_c += CLIENTID 436 | self.topic_s += CLIENTID 437 | client.on_connect = self.on_connect 438 | client.on_message = self.on_message 439 | client.username_pw_set(USERNAME, PASSWORD) 440 | client.will_set(self.topic_s, '>> DEAD:' + self.topic_c, 1) 441 | client.connect(MQTTHOST, MQTTPORT, HEARTBEAT) 442 | 443 | if S_OR_C == 'C': 444 | client.publish(topic=self.topic_s, payload=self.topic_c, qos=0) 445 | client.loop_start() # 线程 446 | # client.loop_forever() # 阻塞 447 | 448 | def sendURL(self, URL): 449 | print(">> 上报URL") 450 | client.publish(topic=self.topic_s, payload=URL, qos=0) 451 | 452 | # 获取外网IP 453 | def GetOuterIP(): 454 | ip = requests.get('https://api.ipify.org/?format=json').json()['ip'] 455 | return str(ip) 456 | 457 | 458 | 459 | REMIND_MSG = ">> MSG:" + "\r\n\r\n** [服务端] 调试中,请勿使用 **\r\n\r\n" # 提示信息 460 | # REMIND_MSG = ">> MSG:" + "\r\n\r\n** 有新版可用(V1.0),旧版不显示版本号,请更新 **\r\n\r\n" 461 | DBPATH = r'ZW.db' # 数据库位置 462 | CLIENTID = GetOuterIP() or "SXF" # MQTT 订阅ID 463 | S_OR_C = 'S' # 客户端/服务端 464 | PRISEQ = '001' # 序列号 465 | # if S_OR_C == 'S': 466 | # CLIENTID = "SXF" 467 | # DBPATH = r'C:\Users\SXF\Desktop\dist\ZW.db' 468 | # if not os.path.isfile(DBPATH): 469 | # DBPATH = r'ZW.db' 470 | client = mqtt.Client(CLIENTID) # MQTT实例 471 | C_NOT_END = 1 # 客户端传输文件未完成 472 | VERSION = '1.0' # 版本号 473 | def main(): 474 | global S_OR_C, PRISEQ 475 | S_OR_C = input("服务端输入S,客户端输入C: ").strip().upper() 476 | PRISEQ = input("输入“序列号”,随便输,只要客户端与服务端对应就能联通了: ").strip() + '/' 477 | if S_OR_C == 'C': 478 | print('*' * 60) 479 | print(">> 当前为【客户端】设备,版本号:", VERSION) 480 | print(">> 仅供学习使用,禁止不良牟利") 481 | print(">> 启动后约等待10s, 30s超时") 482 | print(">> 5次次数使用完,可留言你的IP增加") 483 | print(">> 若右键无法粘贴,在标题栏右击,选属性;勾选“快速编辑模式”") 484 | print(">> github: https://github.com/1061700625/RemoteFileTransfer") 485 | print('*' * 60) 486 | print('\r\n') 487 | mqtt = MQTT() 488 | mqtt.mqtt() 489 | print(">> 等待[服务端]响应") 490 | start_time = time.time() 491 | while True: 492 | if C_CONNECT: 493 | while True: 494 | URL = input('>> 复制后右击输入URL后排队:').strip() 495 | if re.match('http', URL): 496 | break 497 | else: 498 | print(">> URL错误,应为http(s)形式") 499 | mqtt.sendURL(URL) 500 | while 1: 501 | time.sleep(1) 502 | time.sleep(1) 503 | end_time = time.time() 504 | if int(end_time - start_time) > 30: 505 | print(">> [服务端]连接超时, 任意键退出") 506 | input() 507 | os._exit(0) 508 | 509 | elif S_OR_C == 'S': 510 | print(">> 当前为【服务端】设备") 511 | mqtt = MQTT() 512 | mqtt.mqtt() 513 | CreatSendTask_thread = threading.Thread(target=mqtt.CreatSendTask) 514 | CreatSendTask_thread.setDaemon(True) 515 | CreatSendTask_thread.start() 516 | 517 | while 1: 518 | time.sleep(5) 519 | pass 520 | 521 | 522 | if __name__ == '__main__': 523 | ''' 524 | 1. 服务端运行在校园网环境,客户端为用户使用。双方建立连接后,客户端上报知网文章URL,服务端获取文章后,即可下发到客户端。 525 | 2. 搜索下载顺序为:pdf下载 -> caj下载 -> 整本下载 526 | 3. 引入排队下载机制,当有多个用户同时请求下载时,将按照先进先出的原则依次运行。 527 | 4. 同时使用sqlite3数据库对用户进行管理。 528 | 5. 增加设置120s传输超时 529 | 6. 程序运行后可选择服务端或是客户端,也就是说,只要稍加改动源码,就可以将程序DIY为两台PC之间的远程文件传输。如: 530 | a. 办公室电脑运行服务端, 家里电脑运行客户端,即可远程利用公司内网下载权限文件至处于外网环境下的电脑。 531 | b. 服务器器运行服务端,自己电脑运行客户端,即可将服务器上文件下载下来。 532 | 7. 下载完成说明: 533 | 出现“完成,重启软件排队” 534 | 8. 右键无法粘贴的: 535 | 在标题那里右击,选属性;然后把“快速编辑模式”打钩,这样就能右键粘贴输入了 536 | 9. github:https://github.com/1061700625/RemoteFileTransfer 537 | 10. 程序中用到的MQTT服务器,大家可以继续用我的,只不过买的服务器还有一个月就要过期了。程序中用到的MQTT服务器,大家可以继续用我的,只不过买的服务器还有一个月就要过期了。 538 | 11. 为了防止大家各自的程序混乱,所以在打开软件后会要求你输入一个“序列号”,随便输,只要客户端与服务端对应就能联通了。 539 | 12. 如果大家没有校园网,可以跟我联系,我适当运行一下服务端,同时我的序列号就设置为‘SXF’了。 540 | ''' 541 | main() 542 | 543 | --------------------------------------------------------------------------------