我需要在Java SE中实现一个相当简单的WebSocket服务器。它需要做的就是接受连接并存储相应的会话,然后在某个事件被触发时向所有连接的客户端发送消息。
我找不到一个关于如何在常规Java SE中执行此操作的教程。所有这些都需要与Maven一起运行,或者将它部署为WAR - 这对于这个项目来说都是不可能的。我需要将其作为Java SE桌面应用程序运行。
我找到的教程展示了如何使用subjectAltNames
,@OnOpen
和@OnMessage
等注释实现端点。但是,它们都没有解释如何实际初始化服务器。我还需要能够为传入连接指定不同的端口号。
我错过了什么吗?我知道人们使用WebSocket制作聊天应用程序,而且真的不需要Web应用程序服务器。我也没有使用Maven,为了简单起见,我宁愿保持这种方式。
答案 0 :(得分:6)
https://github.com/TooTallNate/Java-WebSocket是Java SE中的完整WebSockets服务器和客户端实现,不需要企业/ Web应用服务器。
答案 1 :(得分:3)
Java 11服务器代码:
package org.treez.server.websocket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;;
public class WebSocketServer{
public static void main(String[] args){
int portNumber = 8000;
ServerSocket server;
try {
server = new ServerSocket(portNumber);
} catch (IOException exception) {
throw new IllegalStateException("Could not create web server", exception);
}
Socket clientSocket;
try {
clientSocket = server.accept(); //waits until a client connects
} catch (IOException waitException) {
throw new IllegalStateException("Could not wait for client connection", waitException);
}
InputStream inputStream;
try {
inputStream = clientSocket.getInputStream();
} catch (IOException inputStreamException) {
throw new IllegalStateException("Could not connect to client input stream", inputStreamException);
}
OutputStream outputStream;
try {
outputStream = clientSocket.getOutputStream();
} catch (IOException inputStreamException) {
throw new IllegalStateException("Could not connect to client input stream", inputStreamException);
}
try {
doHandShakeToInitializeWebSocketConnection(inputStream, outputStream);
} catch (UnsupportedEncodingException handShakeException) {
throw new IllegalStateException("Could not connect to client input stream", handShakeException);
}
try {
outputStream.write(encode("Hello from Server!"));
outputStream.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
printInputStream(inputStream);
} catch (IOException printException) {
throw new IllegalStateException("Could not connect to client input stream", printException);
}
}
//Source for encoding and decoding:
//https://stackoverflow.com/questions/8125507/how-can-i-send-and-receive-websocket-messages-on-the-server-side
private static void printInputStream(InputStream inputStream) throws IOException {
int len = 0;
byte[] b = new byte[1024];
//rawIn is a Socket.getInputStream();
while(true){
len = inputStream.read(b);
if(len!=-1){
byte rLength = 0;
int rMaskIndex = 2;
int rDataStart = 0;
//b[0] is always text in my case so no need to check;
byte data = b[1];
byte op = (byte) 127;
rLength = (byte) (data & op);
if(rLength==(byte)126) rMaskIndex=4;
if(rLength==(byte)127) rMaskIndex=10;
byte[] masks = new byte[4];
int j=0;
int i=0;
for(i=rMaskIndex;i<(rMaskIndex+4);i++){
masks[j] = b[i];
j++;
}
rDataStart = rMaskIndex + 4;
int messLen = len - rDataStart;
byte[] message = new byte[messLen];
for(i=rDataStart, j=0; i<len; i++, j++){
message[j] = (byte) (b[i] ^ masks[j % 4]);
}
System.out.println(new String(message));
b = new byte[1024];
}
}
}
public static byte[] encode(String mess) throws IOException{
byte[] rawData = mess.getBytes();
int frameCount = 0;
byte[] frame = new byte[10];
frame[0] = (byte) 129;
if(rawData.length <= 125){
frame[1] = (byte) rawData.length;
frameCount = 2;
}else if(rawData.length >= 126 && rawData.length <= 65535){
frame[1] = (byte) 126;
int len = rawData.length;
frame[2] = (byte)((len >> 8 ) & (byte)255);
frame[3] = (byte)(len & (byte)255);
frameCount = 4;
}else{
frame[1] = (byte) 127;
int len = rawData.length;
frame[2] = (byte)((len >> 56 ) & (byte)255);
frame[3] = (byte)((len >> 48 ) & (byte)255);
frame[4] = (byte)((len >> 40 ) & (byte)255);
frame[5] = (byte)((len >> 32 ) & (byte)255);
frame[6] = (byte)((len >> 24 ) & (byte)255);
frame[7] = (byte)((len >> 16 ) & (byte)255);
frame[8] = (byte)((len >> 8 ) & (byte)255);
frame[9] = (byte)(len & (byte)255);
frameCount = 10;
}
int bLength = frameCount + rawData.length;
byte[] reply = new byte[bLength];
int bLim = 0;
for(int i=0; i<frameCount;i++){
reply[bLim] = frame[i];
bLim++;
}
for(int i=0; i<rawData.length;i++){
reply[bLim] = rawData[i];
bLim++;
}
return reply;
}
private static void doHandShakeToInitializeWebSocketConnection(InputStream inputStream, OutputStream outputStream) throws UnsupportedEncodingException {
String data = new Scanner(inputStream,"UTF-8").useDelimiter("\\r\\n\\r\\n").next();
Matcher get = Pattern.compile("^GET").matcher(data);
if (get.find()) {
Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
match.find();
byte[] response = null;
try {
response = ("HTTP/1.1 101 Switching Protocols\r\n"
+ "Connection: Upgrade\r\n"
+ "Upgrade: websocket\r\n"
+ "Sec-WebSocket-Accept: "
+ DatatypeConverter.printBase64Binary(
MessageDigest
.getInstance("SHA-1")
.digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
.getBytes("UTF-8")))
+ "\r\n\r\n")
.getBytes("UTF-8");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
outputStream.write(response, 0, response.length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
}
}
}
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>TreezHttpServer</groupId>
<artifactId>TreezHttpServer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</project>
JavaScript客户端:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Client</title>
<script type="text/javascript">
var wsocket;
function connect() {
wsocket = new WebSocket("ws://localhost:8000");
wsocket.onopen = onopen;
wsocket.onmessage = onmessage;
wsocket.onclose = onclose;
}
function onopen() {
console.log("Connected!");
wsocket.send('hello from client');
}
function onmessage(event) {
console.log("Data received: " + event.data);
}
function onclose(e) {
console.log("Connection closed.");
}
window.addEventListener("load", connect, false);
</script>
</head>
<body>
</body>
</html>
答案 2 :(得分:0)
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;;
public class WebSocketServer{
public static void main(String[] args){
int portNumber = 8000;
ServerSocket server;
try {
server = new ServerSocket(portNumber);
} catch (IOException exception) {
throw new IllegalStateException("Could not create web server", exception);
}
Socket clientSocket;
try {
clientSocket = server.accept(); //waits until a client connects
} catch (IOException waitException) {
throw new IllegalStateException("Could not wait for client connection", waitException);
}
InputStream inputStream;
try {
inputStream = clientSocket.getInputStream();
} catch (IOException inputStreamException) {
throw new IllegalStateException("Could not connect to client input stream", inputStreamException);
}
OutputStream outputStream;
try {
outputStream = clientSocket.getOutputStream();
} catch (IOException inputStreamException) {
throw new IllegalStateException("Could not connect to client input stream", inputStreamException);
}
try {
doHandShakeToInitializeWebSocketConnection(inputStream, outputStream);
} catch (UnsupportedEncodingException handShakeException) {
throw new IllegalStateException("Could not connect to client input stream", handShakeException);
}
try {
outputStream.write(encode("Hello from Server!"));
outputStream.flush();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
printInputStream(inputStream);
} catch (IOException printException) {
throw new IllegalStateException("Could not connect to client input stream", printException);
}
}
//Source for encoding and decoding:
//https://stackoverflow.com/questions/8125507/how-can-i-send-and-receive-websocket-messages-on-the-server-side
//this will handle incoming text only up to 64K only
//it will handle multiple messages in one read and messages split over a read
private static void printInputStream(InputStream inputStream) throws IOException {
byte[] b = new byte[8000];//incoming buffer
byte[] message =null;//buffer to assemble message in
byte[] masks = new byte[4];
boolean isSplit=false;//has a message been split over a read
int length = 0; //length of message
int totalRead =0; //total read in message so far
while (true) {
int len = 0;//length of bytes read from socket
try {
len = inputStream.read(b);
} catch (IOException e) {
break;
}
if (len != -1) {
boolean more = false;
int totalLength = 0;
do {
int j = 0;
int i = 0;
if (!isSplit) {
byte rLength = 0;
int rMaskIndex = 2;
int rDataStart = 0;
// b[0] assuming text
byte data = b[1];
byte op = (byte) 127;
rLength = (byte) (data & op);
length = (int) rLength;
if (rLength == (byte) 126) {
rMaskIndex = 4;
length = Byte.toUnsignedInt(b[2]) << 8;
length += Byte.toUnsignedInt(b[3]);
} else if (rLength == (byte) 127)
rMaskIndex = 10;
for (i = rMaskIndex; i < (rMaskIndex + 4); i++) {
masks[j] = b[i];
j++;
}
rDataStart = rMaskIndex + 4;
message = new byte[length];
totalLength = length + rDataStart;
for (i = rDataStart, totalRead = 0; i<len && i < totalLength; i++, totalRead++) {
message[totalRead] = (byte) (b[i] ^ masks[totalRead % 4]);
}
}else {
for (i = 0; i<len && totalRead<length; i++, totalRead++) {
message[totalRead] = (byte) (b[i] ^ masks[totalRead % 4]);
}
totalLength=i;
}
if (totalRead<length) {
isSplit=true;
}else {
isSplit=false;
System.out.println(new String(message));
b = new byte[8000];
}
if (totalLength < len) {
more = true;
for (i = totalLength, j = 0; i < len; i++, j++)
b[j] = b[i];
len = len - totalLength;
}else
more = false;
} while (more);
} else
break;
}
}
public static byte[] encode(String mess) throws IOException{
byte[] rawData = mess.getBytes();
int frameCount = 0;
byte[] frame = new byte[10];
frame[0] = (byte) 129;
if(rawData.length <= 125){
frame[1] = (byte) rawData.length;
frameCount = 2;
}else if(rawData.length >= 126 && rawData.length <= 65535){
frame[1] = (byte) 126;
int len = rawData.length;
frame[2] = (byte)((len >> 8 ) & (byte)255);
frame[3] = (byte)(len & (byte)255);
frameCount = 4;
}else{
frame[1] = (byte) 127;
long len = rawData.length; //note an int is not big enough in java
frame[2] = (byte)((len >> 56 ) & (byte)255);
frame[3] = (byte)((len >> 48 ) & (byte)255);
frame[4] = (byte)((len >> 40 ) & (byte)255);
frame[5] = (byte)((len >> 32 ) & (byte)255);
frame[6] = (byte)((len >> 24 ) & (byte)255);
frame[7] = (byte)((len >> 16 ) & (byte)255);
frame[8] = (byte)((len >> 8 ) & (byte)255);
frame[9] = (byte)(len & (byte)255);
frameCount = 10;
}
int bLength = frameCount + rawData.length;
byte[] reply = new byte[bLength];
int bLim = 0;
for(int i=0; i<frameCount;i++){
reply[bLim] = frame[i];
bLim++;
}
for(int i=0; i<rawData.length;i++){
reply[bLim] = rawData[i];
bLim++;
}
return reply;
}
private static void doHandShakeToInitializeWebSocketConnection(InputStream inputStream, OutputStream outputStream) throws UnsupportedEncodingException {
String data = new Scanner(inputStream,"UTF-8").useDelimiter("\\r\\n\\r\\n").next();
Matcher get = Pattern.compile("^GET").matcher(data);
if (get.find()) {
Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
match.find();
byte[] response = null;
try {
response = ("HTTP/1.1 101 Switching Protocols\r\n"
+ "Connection: Upgrade\r\n"
+ "Upgrade: websocket\r\n"
+ "Sec-WebSocket-Accept: "
+ DatatypeConverter.printBase64Binary(
MessageDigest
.getInstance("SHA-1")
.digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
.getBytes("UTF-8")))
+ "\r\n\r\n")
.getBytes("UTF-8");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
outputStream.write(response, 0, response.length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
}
}
}
这解决了 Stefan 回答的问题。它可以在一次读取中处理多条消息,并将消息拆分为一次读取。还修复了在写入中使用 int 而不是 long 的问题。