JAVA Sokcet(服務端)連線WebSocket個人總結
阿新 • • 發佈:2019-02-03
package com.cx; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import javax.swing.JTextArea; /** * @author(作者) :Cx */ public class WebServer { private int port = 8888; private ServerSocket serverSocket; private List<Handler> handlers = new ArrayList<Handler>(); JTextArea text;//顯示資訊 public WebServer(JTextArea text) throws IOException { this.text = text; serverSocket = new ServerSocket(port); text.setText("WebSocket伺服器已啟動!\n"); text.append("等待客戶端連線...!\n"); } public void service() { Socket socket = null; while (true) { try { //接收socket請求 socket = serverSocket.accept(); //判斷 if(handlers.size()>0){ for (Handler handler : handlers) { if(handler.getSpeech()!=null){ handler.getSpeech().flag=false; } } handlers.clear(); } //開啟執行緒,接收不同的socket請求 Handler handler = new Handler(socket); handlers.add(handler); Thread workThread = new Thread(handler); workThread.start(); } catch (IOException e) { e.printStackTrace(); } } } class Handler implements Runnable { private Speech speech = new Speech(); private Socket socket; private boolean hasHandshake = false; Charset charset = Charset.forName("UTF-8"); public Speech getSpeech(){ return this.speech; } public Handler(Socket socket) { this.socket = socket; } private PrintWriter getWriter(Socket socket) throws IOException { OutputStream socketOut = socket.getOutputStream(); return new PrintWriter(socketOut, true); } public void run() { try { System.out.println("New connection accepted" + socket.getInetAddress() + ":" + socket.getPort()); text.append("New connection accepted!.."+socket.getInetAddress()+":"+socket.getPort()+"\n"); text.setCaretPosition(text.getText().length()); //獲取socket輸入流資訊 InputStream in = socket.getInputStream(); //獲取socket輸出 PrintWriter pw = getWriter(socket); //讀入快取(定義一個1M的快取區) byte[] buf = new byte[1024]; //讀到位元組(讀取輸入流資料到快取) int len = in.read(buf, 0, 1024); //讀到位元組陣列(定義一個容納資料大小合適快取區) byte[] res = new byte[len]; //將buf內中資料拷貝到res中 System.arraycopy(buf, 0, res, 0, len); //列印res快取內容 String key = new String(res); if(!hasHandshake && key.indexOf("Key") > 0){ //握手 //通過字串擷取獲取key值 key = key.substring(0, key.indexOf("==") + 2); key = key.substring(key.indexOf("Key") + 4, key.length()).trim(); //拼接WEBSOCKET傳輸協議的安全校驗字串 key+= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; //通過SHA-1演算法進行更新 MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(key.getBytes("utf-8"), 0, key.length()); byte[] sha1Hash = md.digest(); //進行Base64加密 sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder(); key = encoder.encode(sha1Hash); //伺服器端返回輸出內容 pw.println("HTTP/1.1 101 Switching Protocols"); pw.println("Upgrade: websocket"); pw.println("Connection: Upgrade"); pw.println("Sec-WebSocket-Accept: " + key); pw.println(); pw.flush(); //將握手標誌更新,只握一次 hasHandshake = true; //伺服器返回成功,---握手協議完成,進行TCP通訊 //進行語音互動 //當時做語音時用到的 speech.verifySocket(this); //----------------------以下是獲取到瀏覽器傳送的訊息-------- //接收資料 byte[] first = new byte[1]; //這裡會阻塞 int read = in.read(first, 0, 1); //讀取第一個位元組是否有值,開始接收資料 while(read > 0){ //讓byte和十六進位制做與運算(二進位制也就是11111111) //獲取到第一個位元組的數值 int b = first[0] & 0xFF; //1為字元資料,8為關閉socket(只要低四位的值判斷) byte opCode = (byte) (b & 0x0F); if(opCode == 8){ socket.getOutputStream().close(); break; } b = in.read(); //只能描述127 int payloadLength = b & 0x7F; if (payloadLength == 126) { byte[] extended = new byte[2]; in.read(extended, 0, 2); int shift = 0; payloadLength = 0; for (int i = extended.length - 1; i >= 0; i--) { payloadLength = payloadLength + ((extended[i] & 0xFF) << shift); shift += 2; } } else if (payloadLength == 127) { byte[] extended = new byte[8]; in.read(extended, 0, 8); int shift = 0; payloadLength = 0; for (int i = extended.length - 1; i >= 0; i--) { payloadLength = payloadLength + ((extended[i] & 0xFF) << shift); shift += 8; } } //掩碼 byte[] mask = new byte[4]; in.read(mask, 0, 4); int readThisFragment = 1; ByteBuffer byteBuf = ByteBuffer.allocate(payloadLength + 30); byteBuf.put("瀏覽器: ".getBytes("UTF-8")); while(payloadLength > 0){ int masked = in.read(); masked = masked ^ (mask[(int) ((readThisFragment - 1) % 4)] & 0xFF); byteBuf.put((byte) masked); payloadLength--; readThisFragment++; } byteBuf.flip(); //將內容返回給客戶端 responseClient(byteBuf, true); //列印內容 printRes(byteBuf.array()); in.read(first, 0, 1); } } in.close(); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("*********************廢棄Socket回收階段******************!"); try { if (socket != null){ socket.close(); } } catch (IOException e) { e.printStackTrace(); } } } public void responseClient(ByteBuffer byteBuf, boolean finalFragment) throws IOException { OutputStream out = socket.getOutputStream(); int first = 0x00; //是否是輸出最後的WebSocket響應片段 if (finalFragment) { first = first + 0x80; first = first + 0x1; } out.write(first); if (byteBuf.limit() < 126) { out.write(byteBuf.limit()); } else if (byteBuf.limit() < 65536) { out.write(126); out.write(byteBuf.limit() >>> 8); out.write(byteBuf.limit() & 0xFF); } else { // Will never be more than 2^31-1 out.write(127); out.write(0); out.write(0); out.write(0); out.write(0); out.write(byteBuf.limit() >>> 24); out.write(byteBuf.limit() >>> 16); out.write(byteBuf.limit() >>> 8); out.write(byteBuf.limit() & 0xFF); } // Write the content out.write(byteBuf.array(), 0, byteBuf.limit()); out.flush(); } private void printRes(byte[] array) { ByteArrayInputStream byteIn = new ByteArrayInputStream(array); InputStreamReader reader = new InputStreamReader(byteIn, charset.newDecoder()); int b = 0; String res = ""; try { while((b = reader.read()) > 0){ res += (char)b; } } catch (IOException e) { e.printStackTrace(); } System.out.println(res); } } }