不带JEE/应用服务器的独立WebSocket服务器

aij0ehis  于 5个月前  发布在  其他
关注(0)|答案(4)|浏览(49)

我需要在Java SE中实现一个相当简单的WebSocket服务器。它所需要做的就是接受连接并存储相应的会话,然后在触发某个事件时向所有连接的客户端发送消息。
我找不到一个关于如何在普通Java SE中实现这一点的教程。所有这些都需要使用Maven运行,或者将其部署为WAR -这对于这个项目来说都是不可能的。我需要将其作为Java SE桌面应用程序运行。
我找到的教程展示了如何使用@OnOpen@OnMessage@OnClose等注解实现端点。然而,它们都没有解释如何实际初始化服务器。我还需要能够为传入连接指定不同的端口号。
我错过了什么吗?我知道人们已经用WebSocket开发了聊天应用程序,这真的不需要Web应用程序服务器。我也没有使用Maven,为了简单起见,我宁愿保持这种方式。

bweufnob

bweufnob1#

https://github.com/TooTallNate/Java-WebSocket是Java SE中的完整WebSockets服务器和客户端实现,不需要企业/Web应用服务器。

68de4m5k

68de4m5k2#

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>

vfhzx4xs

vfhzx4xs3#

尝试使用Stefan的代码。目前它不能很好地工作(我使用Firefox)。大于1024字节的WebSocket数据包被分割成TCP段,因此需要重新组装。下面是阅读浏览器数据包的更新代码:

private static void processResponse(InputStream inputStream, OutputStream outputStream) throws IOException {
    int readPacketLength = 0;
    byte[] packet = new byte[1024];
    ByteArrayOutputStream packetStream = new ByteArrayOutputStream();

    while(true) {
        readPacketLength = inputStream.read(packet);

        if(readPacketLength != -1) {
            if ((packet[0] & (byte) 15) == (byte) 8) { // Disconnect packet
                outputStream.write(packet, 0, readPacketLength);
                // returning the same packet for client to terminate connection
                outputStream.flush();
                return;
            }
            byte messageLengthByte = 0;
            int messageLength = 0;
            int maskIndex = 2;
            int messageStart = 0;
            //b[0] is always text in my case so no need to check;
            byte data = packet[1];
            byte op = (byte) 127; // 0111 111
            messageLengthByte = (byte) (data & op);

            int totalPacketLength = 0;
            if (messageLengthByte == (byte) 126 || messageLengthByte == (byte) 127) {
                if (messageLengthByte == (byte) 126) {
                    maskIndex = 4;
                    // if (messageLengthInt==(byte)126), then 16-bit length is stored in packet[2] and [3]
                    ByteBuffer messageLength16Bit = ByteBuffer.allocateDirect(4);
                    messageLength16Bit.order(ByteOrder.BIG_ENDIAN);
                    messageLength16Bit.put((byte) 0x00);
                    messageLength16Bit.put((byte) 0x00);
                    messageLength16Bit.put(packet, 2, 2);
                    messageLength16Bit.flip();
                    messageLength = messageLength16Bit.getInt();
                    totalPacketLength = messageLength + 8;
                } else {
                    maskIndex = 10;
                    // if (messageLengthInt==(byte)127), then 64-bit length is stored in bytes [2] to [9]. Using only 32-bit
                    ByteBuffer messageLength64Bit = ByteBuffer.allocateDirect(4);
                    messageLength64Bit.order(ByteOrder.BIG_ENDIAN);
                    messageLength64Bit.put(packet, 6, 4);
                    messageLength64Bit.flip();
                    messageLength = messageLength64Bit.getInt();
                    totalPacketLength = messageLength + 14;
                }

                if (readPacketLength != totalPacketLength) {
                    packetStream.write(packet, 0, readPacketLength);

                    int lastPacketLength = 0;
                    while (readPacketLength < totalPacketLength) {
                        packet = new byte[1024];
                        readPacketLength += lastPacketLength = inputStream.read(packet);
                        packetStream.write(packet, 0, lastPacketLength);
                    }
                    packet = packetStream.toByteArray();
                    packetStream.reset();
                }
            }
            else { // using message length from packet[1]
                messageLength = messageLengthByte;
            }

            byte[] masks = new byte[4];
            int i=0; int j=0;
            for(i = maskIndex; i < (maskIndex+4); i++) {
                masks[j] = packet[i];
                j++;
            }

            messageStart = maskIndex + 4;

            byte[] message = new byte[messageLength];
            for(i = messageStart, j = 0; i < readPacketLength; i++, j++){
                message[j] = (byte) (packet[i] ^ masks[j % 4]);
            }
            System.out.println("Received message: " + new String(message));
            packet = new byte[1024];
        }
    }
}

字符串

hujrc8aj

hujrc8aj4#

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的问题。

相关问题