最近突然想到了一个可以绕开单位安全管控软件,让单位内部办公电脑连上外网的方法。大概是这个样子,让单位办公电脑与自己的外网电脑进行蓝牙配对,然后用配对成功的蓝牙进行网络数据交互。这里大家可能会想用一下蓝牙的网络共享功能,分分钟不就实现了,其实这里是有问题的,因为这样会在单位内部办公电脑上虚拟出一个网卡,马上会被单位安全管控软件识别,进而被网络管理员发现,至少我们单位是这样的,所以不能这样用,我这里用Java写了一个蓝牙数据通讯的程序,同时考虑到蓝牙数据通讯较慢,直接用浏览器访问太慢,又用Python爬了几个自己经常访问的网站,用爬虫只访问有用信息,减少蓝牙数据通讯的数据量,最后总体感觉相当不错。下面以办公电脑连接外网实现中英文翻译为例进行介绍。拓扑图如下:
蓝牙数据交换功能用Java语言实现,其中用到了[bluecove-2.1.1.jar]蓝牙功能操作包。客户端安装在内网电脑上(比如办公电脑),在接收到内网电脑访问外部网络访的Socket请求后,自动与外网电脑进行蓝牙连接,并将Socket通讯数据转为蓝牙通讯数据,镜像至外网,主要代码如下:
public class SocketServer {
private String bluetoothRemoteUrl = null;
public SocketServer() {}
public void start() throws IOException {
try (ServerSocket server = new ServerSocket(SocketConfig.SERVER_PORT,SocketConfig.SERVER_BACKLOG,
InetAddress.getByName(SocketConfig.SERVER_ADDRESS))) {
System.out.print("Socket通讯监听[" + SocketConfig.SERVER_ADDRESS + ":" + SocketConfig.SERVER_PORT + "]启动成功...");
ExecutorService service = Executors.newFixedThreadPool(BluetoothConfig.SERVICE_POOL);
System.out.println("服务线程池[" + BluetoothConfig.SERVICE_POOL + "]初始化成功...");
Socket socket = null;
BluetoothChannel channel = null;
while(true) {
try {
socket = server.accept();
System.out.println("客户端[" + socket.getInetAddress().getHostAddress() + "]已连接...");
}
catch(Exception e) {
System.out.println("客户端连接错误[" + e.getMessage() + "]");
if (socket != null) {
socket.close();
}
continue;
}
System.out.print("开始与蓝牙服务[" + BluetoothConfig.REMOTE_UUID + "@" + BluetoothConfig.REMOTE_ADDRESS + "]建立连接...");
try {
if (StrUtil.isBlank(bluetoothRemoteUrl)) {
bluetoothRemoteUrl = BluetoothTools.fetchRemoteUrl(BluetoothConfig.REMOTE_ADDRESS,
new UUID(BluetoothConfig.REMOTE_UUID,true),ServiceRecord.NOAUTHENTICATE_NOENCRYPT,false);
}
channel = BluetoothTools.open(bluetoothRemoteUrl);
System.out.println("已连接");
}
catch (Exception e) {
System.out.println("连接错误[" + e.getMessage() + "]");
if (socket != null) {
socket.close();
}
if (channel != null) {
channel.close();
}
continue;
}
service.execute(new SendService(socket,channel));
service.execute(new RecvService(socket,channel));
}
}
}
服务端安装在外网电脑,在接收到客户端蓝牙连接请求后,自动与代理服务器(或目标网站)进行Socket连接,并重新将蓝牙通讯数据转为Socket通讯数据,发送至代理服务器(或目标网站),然后接收代理服务器(或目标网站)响应,镜像至客户端,主要服务端代码如下:
public class BluetoothServer {
public BluetoothServer() {}
public void start() throws BluetoothStateException,IOException {
LocalDevice bluetooth = LocalDevice.getLocalDevice();
bluetooth.setDiscoverable(DiscoveryAgent.GIAC);
StreamConnectionNotifier notifier = (StreamConnectionNotifier)Connector.open(
"btspp://localhost:" + BluetoothTools.genUuid(BluetoothConfig.SERVICE_UUID, true)
+ ";name=" + BluetoothConfig.SERVICE_NAME);
System.out.print("蓝牙[" + bluetooth.getFriendlyName() + "]"
+ "通讯监听[" + BluetoothConfig.SERVICE_UUID + "@" + bluetooth.getBluetoothAddress() + "]启动成功...");
ExecutorService service = Executors.newFixedThreadPool(BluetoothConfig.SERVICE_POOL);
System.out.println("服务线程池[" + BluetoothConfig.SERVICE_POOL + "]初始化成功...");
BluetoothChannel channel = null;
RemoteDevice device = null;
Socket socket = null;
while(true) {
try {
channel = new BluetoothChannel(notifier.acceptAndOpen());
device = RemoteDevice.getRemoteDevice(channel.getStreamConnection());
System.out.println("客户端蓝牙[" + device.getFriendlyName(true) + "]"
+ "[" + device.getBluetoothAddress() + "]已连接...");
}
catch (Exception e) {
System.out.println("客户端蓝牙连接错误[" + e.getMessage() + "]");
if (channel != null) {
channel.close();
}
continue;
}
System.out.print("开始与目标服务器[" + SocketConfig.TARGET_ADDRESS + ":" + SocketConfig.TARGET_PORT + "]建立连接...");
try {
socket = new Socket();
socket.connect(new InetSocketAddress(SocketConfig.TARGET_ADDRESS,SocketConfig.TARGET_PORT),SocketConfig.TARGET_COTIMEOUT);
socket.setSoTimeout(SocketConfig.TARGET_SOTIMEOUT);
System.out.println("已连接");
}
catch (Exception e) {
System.out.println("连接错误[" + e.getMessage() + "]");
if (socket != null) {
socket.close();
}
if (channel != null) {
channel.close();
}
continue;
}
service.execute(new SendService(socket,channel));
service.execute(new RecvService(socket,channel));
}
}
}
蓝牙数据流处理主要代码如下:
public class BluetoothChannel implements Closeable {
private StreamConnection connection = null;
private DataInputStream dInStrm = null;
private DataOutputStream dOutStrm = null;
private boolean closed = true;
public BluetoothChannel(StreamConnection connection)
throws IOException {
this.connection = connection;
this.dInStrm = connection.openDataInputStream();
this.dOutStrm = connection.openDataOutputStream();
this.closed = false;
}
public DataInputStream getDataInputStream() {
return this.dInStrm;
}
public DataOutputStream getDataOutputStream() {
return this.dOutStrm;
}
public boolean isClosed() {
return this.closed;
}
@Override
public void close() throws IOException {
dOutStrm.close();
dInStrm.close();
connection.close();
closed = true;
}
}
数据接收主要代码下:
public class RecvService implements Runnable {
private Socket socket = null;
private BluetoothChannel channel = null;
public RecvService(Socket socket,BluetoothChannel channel) {
this.socket = socket;
this.channel = channel;
}
@Override
public void run() {
try {
InputStream in = channel.getDataInputStream();
OutputStream out = new DataOutputStream(socket.getOutputStream());
int len = 0;
byte[] data = new byte[CommonConfig.STREAM_BUFFER];
try {
while((len = in.read(data,0,CommonConfig.STREAM_BUFFER)) != -1) {
out.write(data,0,len);
out.flush();
}
} catch (IOException e) {}
if (!channel.isClosed()) {
channel.close();
}
if (!socket.isClosed()) {
socket.close();
}
}
catch (Exception e) {
System.out.println("数据通讯处理错误[" + e.getMessage() + "]");
e.printStackTrace();
}
}
}
数据发送主要代码如下:
public class SendService implements Runnable {
private Socket socket = null;
private BluetoothChannel channel = null;
public SendService(Socket socket,BluetoothChannel channel) {
this.socket = socket;
this.channel = channel;
}
@Override
public void run() {
try {
InputStream in = new DataInputStream(socket.getInputStream());
OutputStream out = channel.getDataOutputStream();
int len = 0;
byte[] data = new byte[CommonConfig.STREAM_BUFFER];
try {
while ((len = in.read(data,0,CommonConfig.STREAM_BUFFER)) != -1) {
out.write(data,0,len);
out.flush();
}
}
catch (IOException e) {}
if (!socket.isClosed()) {
socket.close();
}
if (!channel.isClosed()) {
channel.close();
}
}
catch (Exception e) {
System.out.println("数据通讯处理错误[" + e.getMessage() + "]");
e.printStackTrace();
}
}
}
注:以上java程序只是实现通过蓝牙通讯实现内外网数据的镜像,没有代理功能,所以如果要实现对外网网站的访问还需要在外网电脑上安装代理服务器(可以使用目前较为流行的开源代理服务器nginx)。
中英文翻译应用为用Python写的一段爬虫程序,使用cmd命令行下打开(python lexi.py),自动识别中英文,输入中文翻译成英文,输入英文翻译成中文。主要用到了requests包。具体代码如下:
# -*- coding:utf-8 -*-
import json
import re
import requests
headers = {
'accept':'application/json, text/plain, */*',
'accept-encoding':'gzip, deflate, br, zstd',
'accept-language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'connection':'keep-alive',
'cookie':'ABTEST=0|1746782609|v17; SUID=1551A20B3E50A20B00000000681DC991; wuid=1746782609372; SUV=1746782609241; SNUID=93D7248D8680B23602BCFFD687A0005C; translate.sess=4b4c4608-becd-44e0-987c-d4e520a81c55; SGINPUT_UPSCREEN=1746782613767; FQV=d0ca8207c4cbb93a9ca15fda8d652a86',
'host':'fanyi.sogou.com',
'origin':'http://fanyi.sogou.com',
'referer':'http://fanyi.sogou.com/text',
'sec-ch-ua':'"Chromium";v="136", "Microsoft Edge";v="136", "Not.A/Brand";v="99"',
'sec-ch-ua-mobile':'?0',
'sec-ch-ua-platform':'"Windows"',
'sec-fetch-dest':'empty',
'sec-fetch-mode':'cors',
'sec-fetch-site':'same-origin',
'content-type':'application/json; charset=utf-8',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0'
}
pattern = re.compile(pattern = r'<script>window\.__INITIAL_STATE__=(?P<data>.*?);\(function\(\){.*?</script>',flags = re.S) #爬取翻译数据正则
proxies = {
'http': 'http://xx.xx.xx.xx:xxxx' #填写安装在内网的客户端地址
}
def http_get(keyword,trans_to):
response = requests.get(url = f'http://fanyi.sogou.com/text?keyword={keyword}&transfrom=auto&transto={trans_to}&model=general',headers = headers,proxies = proxies)
response.encoding = 'utf-8'
text = None
if (response.status_code == 200):
text = response.text
response.close()
return text
def chinese_to_english(json):
print('词典释义:')
for mean in json['textTranslate']['translateData']['wordCard']['secondQuery']:
print(f' {mean['k']:<20}{mean['v']}')
print('\n翻译结果:')
print(f' {json['textTranslate']['translateData']['sentencesData']['data']['trans_result'][0]['trans_text']}')
def english_to_chinese(json):
print('读音:')
voices = json['voice']['from'].get('list')
if (voices):
for voice in voices:
print(f' {voice['abbr']}[{voice['phonetic']}]',end='')
print('\n词典释义:')
for mean in json['textTranslate']['translateData']['wordCard']['usualDict']:
print(f' {mean['pos']:<10}{mean['values'][0]}')
print('\n翻译结果:')
print(f' {json['textTranslate']['translateData']['translate']['dit']}')
def is_chinese_char(char):
#根据Unicode编码范围,判断是否是中文字符
if (char >= '\u4e00' and char <= '\u9fff') \
or (char >= '\u3400' and char <= '\u4dbf') \
or (char >= '\u20000' and char <= '\u2a6df') \
or (char >= '\u3000' and char <= '\u303f'):
return True
else:
return False
def is_chinese_str(str):
#统计字符串中的中文字符数
count = sum(1 for c in str if is_chinese_char(c))
if (len(str) / 2 <= count): #中文字符数占主导认为是中文(中文字符超过一半)
return True
else:
return False
if (__name__ == '__main__'):
print('翻译程序已启动...按[.]或[。]退出...')
while (True):
keyword = input('>')
if ('.' == keyword or '。' == keyword): #按下 . 或 。 退出应用
exit(0)
if (is_chinese_str(keyword)):
#通过正则提取翻译结果数据
data = pattern.search(string = http_get(keyword = keyword, trans_to = 'en')).group('data')
if (not data):
print('响应数据异常')
exit(10)
chinese_to_english(json.loads(data))
else:
#通过正则提取翻译结果数据
data = pattern.search(string = http_get(keyword = keyword, trans_to = 'zh-CHS')).group('data')
if (not data):
print('响应数据异常')
exit(10)
english_to_chinese(json.loads(data))
整个程序效果如下: