用java编写一个websocket端口转发工具

编写背景

工作原因,需要使用到内网,项目组的几个同事都要用内网,然后,甲方问上级申请了内网服务器。但是这个内网服务器需要通过堡垒机访问,而堡垒机只能用指定的两个内网ip访问。本来,两个ip是够用的,但是甲方有个领导是技术出身,就让我们在他的电脑上给他配置堡垒机的访问环境。然后,两个ip就被甲方领导占用了一个。然后,就导致内网ip不够用,我和同事经常等对方用完。
而资源池对内网开放了应用服务器的端口来供内网访问应用,堡垒机上只能编辑nginx的配置,虽然nginx支持tcp协议代理,但是需要安装单独的模块,内网显然是无法实现的。
所以,我就在网上搜索,有没有用websocket转发端口的程序,查了一下之后,发现一个go写的,又因为,资源池的服务器是没办法装go环境的,所以,我选择自己用java实现。

实现原理

我先用客户端监听本地的某一个端口,在有连接请求之后,客户端会生成一个随机的密钥,用公钥加密之后发送到服务器端,然后,服务器端和客户端之间的所有信息都会使用密钥加密通讯。socketServer收到的所有信息都加密后通过webSocket发送到服务端,而服务端又将数据解密后发送到目标ip和端口,再将从目标ip端口接收到的数据都加密了之后通过webSocket发送给客户端,客户端解密数据之后将数据反馈到本地的端口,然后就实现了资源池的端口加密映射到本地端口。数据库一类的端口映射出来后,就可以通过工具连接了,网页也可以映射,测试也可以正常访问。常见的应用的网络通讯,基本都是基于tcp协议的,所以,这样映射,基本可以满足日常开放。也就不用和同事争ip了。

核心代码

服务端

@Component
@Slf4j
@ServerEndpoint("/ws")
public class WSTServer {
	
	
	private Session session;
	
	private int step = 0;
	
	private String k;
	private String host;
	private int port;
	
	private Socket socket;
	
	private OutputStream out;
	
	private DES des;
	
	@OnOpen
	public void onOpen(Session session) {
		this.session = session;
		this.step = 0;
		log.info("连接建立");
	}
	
	@OnMessage
	public void strMsg(String msg) {
		
		switch(step) {
			
		case 0:
			//密钥交换
			swKey(msg);
			break;
		case 1:
			//建立隧道
			buildLink(msg);
			break;
		}
	}
	
	@OnMessage 
	public void byteMsg(byte[] b) {
		if( this.step == 2 ) {
			byte[] sendByte = this.des.decrypt(b);
			try {
				this.out.write(sendByte);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@OnClose
	public void onClose() {
		if(this.socket != null) {
			try {
				this.session.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void buildLink(String msg) {
		
		try {
			
			String url = des.decryptStr(msg);
			List<String> pms = StrUtil.split(url, ":");
			
			this.host = pms.get(0);
			this.port = Integer.valueOf(pms.get(1));
			
			this.socket = new Socket(host, this.port);
			
			this.out = socket.getOutputStream();
			
			new Thread(()->{
				
				try {
					
					
					try(
						InputStream input = socket.getInputStream();
					){
						
						while(socket.isConnected()) {
							
							byte[] b = new byte[1024];
							int len = 0;
							while( ( len = input.read(b) ) > 0) {
								byte[] send = new byte[len];
								System.arraycopy(b, 0, send, 0, len);
								this.session.getBasicRemote().sendBinary(ByteBuffer.wrap(des.encrypt(send)));
							}
						}
						
					}
					
				}catch (IOException e) {
					e.printStackTrace();
				}
				
			}).start();
			
			sendMsg("success");
			this.step++;
			
			log.info("隧道建立成功");
		}catch (Exception e) {
			e.printStackTrace();
			log.info("隧道构建失败");
			close();
		}
		
	}
	
	private void swKey(String msg) {
		try {
			//获取客户端发来的密钥
			this.k = RsaUtil.decodeHex(msg);
			this.des = new DES(HexUtil.decodeHex(this.k));
			this.step++;
			sendMsg("success");
			log.info("密钥获取完毕");
		}catch (Exception e) {
			// 密钥获取失败,中断连接
			e.printStackTrace();
			log.info("密钥获取失败");
			close();
		}
	}
	
	private void sendMsg(String msg) {
		if(this.des != null) {
			try {
				this.session.getBasicRemote().sendText(this.des.encryptHex(msg));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void close() {
		try {
			this.session.close();
		} catch (IOException e1) {}
	}

客户端

public class WSClient {

	public static void main(String[] args) {
		
		String pub = FileUtil.readString("pub", CharsetUtil.CHARSET_GBK);
		RSA rsa = new RSA(null,pub);
		
		try (
				ServerSocket server = new ServerSocket(Integer.valueOf(args[0]));
			){
			
			while(true) {
				
				Socket skt = server.accept();
				
				InputStream in = skt.getInputStream();
				OutputStream out = skt.getOutputStream();
				
				new Thread( () -> {
					buildLink(args[1],args[2],in, out, new DES(), rsa);
				} ).start();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	public static void buildLink(String tar,String ws,InputStream input, OutputStream out,DES des,RSA rsa) {
		
		try {
			
			AtomicBoolean wconnect = new AtomicBoolean(true);
			
			WebSocketClient client = new WebSocketClient(new URI(ws)) {

				@Override
				public void onOpen(ServerHandshake handshakedata) {
					
				}

				@Override
				public void onMessage(String message) {
					//接收到回复后,发送需要建立的连接
					wconnect.set(false);
				}
				
				@Override
				public void onMessage(ByteBuffer bytes) {
					byte[] b = des.decrypt(bytes.array());
					try {
						out.write(b);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

				@Override
				public void onError(Exception ex) {

				}

				@Override
				public void onClose(int code, String reason, boolean remote) {

				}
			};
			
			client.connectBlocking();
			
			//建立连接后,发送信息
			client.send(rsa.encryptHex(HexUtil.encodeHexStr(des.getSecretKey().getEncoded()), KeyType.PublicKey));
			
			while(wconnect.get()) {
				Thread.sleep(200);
			}
			
			//收到回复后,发送连接目标
			client.send(des.encryptHex(tar));
			wconnect.set(true);
			
			while(wconnect.get()) {
				Thread.sleep(200);
			}
			
			//发送心跳包,防止会话过期
			new Thread( () -> {
				while(true) {
					client.send("123");
					ThreadUtil.safeSleep(1000*5);
				}
			}).start();
			
			//收到连接建立后,建立本地连接
			while(true) {
				
				byte[] b = new byte[1024];
				int len = 0;
				
				while((len = input.read(b) ) > 0) {
					
					byte[] send = new byte[len];
					System.arraycopy(b, 0, send, 0, len);
					
					client.send(des.encrypt(send));
				}
			}
			
		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

项目资源

代码已在gitee上开源,以下是开源仓库地址
服务端
客户端

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java可以使用Java-WebSocket库来编写多个WebSocket实例。在使用Java-WebSocket库时,需要创建多个WebSocketServer实例,并为每个实例指定不同的端口号。每个WebSocketServer实例都可以处理多个WebSocket连接。在处理WebSocket连接时,可以使用WebSocket类的onOpen、onClose、onMessage和onError方法来处理WebSocket连接的打开、关闭、消息和错误事件。此外,还可以使用WebSocket类的send方法向客户端发送消息。下面是一个简单的Java-WebSocket多个WebSocket实例的示例代码: ``` import java.net.InetSocketAddress; import java.util.concurrent.CopyOnWriteArrayList; import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; public class MultiWebSocketServer { private CopyOnWriteArrayList<WebSocket> webSockets = new CopyOnWriteArrayList<>(); public MultiWebSocketServer(int... ports) { for (int port : ports) { WebSocketServer server = new WebSocketServer(new InetSocketAddress(port)) { @Override public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) { webSockets.add(webSocket); System.out.println("WebSocket opened: " + webSocket.getRemoteSocketAddress()); } @Override public void onClose(WebSocket webSocket, int i, String s, boolean b) { webSockets.remove(webSocket); System.out.println("WebSocket closed: " + webSocket.getRemoteSocketAddress()); } @Override public void onMessage(WebSocket webSocket, String s) { System.out.println("WebSocket message received: " + s); } @Override public void onError(WebSocket webSocket, Exception e) { System.out.println("WebSocket error: " + e.getMessage()); } }; server.start(); System.out.println("WebSocket server started on port " + port); } } public void broadcast(String message) { for (WebSocket webSocket : webSockets) { webSocket.send(message); } } public static void main(String[] args) { MultiWebSocketServer server = new MultiWebSocketServer(8080, 8081); server.broadcast("Hello, world!"); } } ``` 此代码创建了两个WebSocketServer实例,分别监听8080和8081端口。在每个WebSocketServer实例的onOpen、onClose、onMessage和onError方法中,分别处理WebSocket连接的打开、关闭、消息和错误事件。在broadcast方法中,使用WebSocket类的send方法向所有客户端发送消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值