You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
11 KiB
286 lines
11 KiB
import javax.json.Json;
|
|
import javax.json.JsonObject;
|
|
import javax.xml.bind.DatatypeConverter;
|
|
import java.io.*;
|
|
import java.net.*;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.Scanner;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
* Created by itsmy on 08.08.2016.
|
|
*/
|
|
public class client extends Thread {
|
|
private Socket socket;
|
|
private OutputStream out;
|
|
private InputStream in;
|
|
private Integer id;
|
|
|
|
public void openSocket(Socket s) throws IOException {
|
|
socket = s;
|
|
out = socket.getOutputStream();
|
|
in = socket.getInputStream();
|
|
id = socketInfo.getNewID();
|
|
console.debug("Create new socket "+ id);
|
|
socketInfo.create(this,id);
|
|
start();
|
|
}
|
|
|
|
|
|
public void run() {
|
|
console.socket(id,"New connection");
|
|
wsControl();
|
|
}
|
|
|
|
public void close(){
|
|
try {
|
|
in.close();
|
|
out.close();
|
|
socket.close();
|
|
}catch (IOException e){
|
|
}
|
|
console.socket(id,"Disconnected");
|
|
socketInfo.close(id);
|
|
}
|
|
|
|
public void wsControl(){
|
|
handshake();
|
|
if(!socketInfo.isHttpAuth(id)){
|
|
return;
|
|
}
|
|
while (socketInfo.isOnline(id)) {
|
|
String msg = readSocket();
|
|
sendSocket(msg);
|
|
//TODO: сделать проверку протокола
|
|
}
|
|
}
|
|
|
|
private String bytesToStringUTFCustom(int[] ints) {
|
|
char[] buffer = new char[ints.length];
|
|
for(int i = 0; i < buffer.length; i++) {
|
|
char c = (char) ints[i];
|
|
buffer[i] = c;
|
|
}
|
|
return new String(buffer);
|
|
}
|
|
|
|
private void handshake(){
|
|
try{
|
|
if(!socketInfo.isHttpAuth(id)) {
|
|
socket.setSoTimeout(2000);
|
|
String data = new Scanner(in, "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();
|
|
try {
|
|
byte[] 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");
|
|
out.write(response, 0, response.length);
|
|
} catch (IOException e) {
|
|
console.out("Server Error:", e.toString());
|
|
} catch (NoSuchAlgorithmException e) {
|
|
console.out("Server Error:", e.toString());
|
|
}
|
|
socket.setSoTimeout(0);
|
|
socketInfo.setHttpAuth(id, true);
|
|
console.socket(id,"Handshake accepted!");
|
|
}
|
|
}
|
|
}catch (SocketException e){
|
|
console.debug("исключение 22");
|
|
//TODO: написать исключение если сокет закрыт
|
|
}catch (NoSuchElementException e){
|
|
console.socket(id,"Handshake Timeout (2000ms)");
|
|
socketInfo.setHttpAuth(id, false);
|
|
}
|
|
}
|
|
|
|
public Boolean sendSocket(String message){
|
|
byte[] response;
|
|
byte[] r;
|
|
try{
|
|
response = message.getBytes("UTF-8");
|
|
}catch (UnsupportedEncodingException e){
|
|
console.socket(id, "Unsupported encoding!");
|
|
return false;
|
|
}
|
|
catch (NullPointerException e){
|
|
return false;
|
|
}
|
|
if (message.length() < 126) {
|
|
r = new byte[response.length+2];
|
|
System.arraycopy(response,0,r,2,response.length);
|
|
r[0] = (byte) 129;
|
|
r[1] = (byte) (response.length);
|
|
}
|
|
else{
|
|
if (message.length() < 65535) {
|
|
r = new byte[response.length+4];
|
|
System.arraycopy(response,0,r,4,response.length);
|
|
r[0] = (byte) 129;
|
|
r[1] = (byte) 126;
|
|
r[2] = (byte) (response.length >>> 8);
|
|
r[3] = (byte) (response.length);
|
|
}
|
|
else{
|
|
if(response.length > Long.MAX_VALUE){
|
|
console.out("Socket Exception", "Too long message for sending! (>64bit)");
|
|
return false;
|
|
}
|
|
r = new byte[response.length+8];
|
|
System.arraycopy(response,0,r,8,response.length);
|
|
r[0] = (byte) 129;
|
|
r[1] = (byte) 127;
|
|
r[2] = (byte) (response.length >>> 56);
|
|
r[3] = (byte) (response.length >>> 48);
|
|
r[4] = (byte) (response.length >>> 40);
|
|
r[5] = (byte) (response.length >>> 32);
|
|
r[6] = (byte) (response.length >>> 24);
|
|
r[7] = (byte) (response.length >>> 16);
|
|
r[8] = (byte) (response.length >>> 8);
|
|
r[9] = (byte) (response.length);
|
|
}
|
|
}
|
|
try{
|
|
out.write(r, 0, r.length);
|
|
return true;
|
|
}catch(IOException e){
|
|
console.out("Socket Exeption", "Client socket is closed?! Closing socket connection!");
|
|
close();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public String readSocket(){
|
|
String out = "";
|
|
try
|
|
{
|
|
int[] MSG = new int[0];
|
|
int OPCOUNT = 1;
|
|
int LENGTH;
|
|
int KEYFRAME = 0;
|
|
int[] KEY = new int[4];
|
|
int OPCODE = in.read(); //читаем OPCODE byte
|
|
switch(OPCODE){
|
|
case -1: close(); //закрываем чтение потока при -1
|
|
return null;
|
|
case 136: close();
|
|
return null;
|
|
case 129:
|
|
LENGTH = in.read(); //читаем второй бит с данными о размере сообщения
|
|
//TODO: which и switch OPCOUNT - необходимо убрать
|
|
while(OPCOUNT < 4) {
|
|
switch (OPCOUNT){
|
|
case 1: switch (LENGTH){ //узнаем длину сообщения
|
|
default: KEYFRAME = 2;
|
|
break;
|
|
case 254: KEYFRAME = 4; //126-65535
|
|
break;
|
|
case 255: KEYFRAME = 10; //65535-...
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (KEYFRAME) {
|
|
case 2:
|
|
MSG = new int[LENGTH - 128];
|
|
break;
|
|
case 4:
|
|
LENGTH = (in.read() << 8) + (in.read());
|
|
MSG = new int[LENGTH];
|
|
break;
|
|
case 10:
|
|
LENGTH = (in.read() << 56)
|
|
+ (in.read() << 48)
|
|
+ (in.read() << 40)
|
|
+ (in.read() << 32)
|
|
+ (in.read() << 24)
|
|
+ (in.read() << 16)
|
|
+ (in.read() << 8)
|
|
+ (in.read());
|
|
MSG = new int[LENGTH];
|
|
break;
|
|
}
|
|
for (int i = 0; i < 4; i++) {
|
|
KEY[i] = in.read();
|
|
}
|
|
for (int j = 0; j < (MSG.length); j++) {
|
|
int enc = in.read();
|
|
MSG[j] = (byte) (enc ^ KEY[j & 0x3]);
|
|
}
|
|
break;
|
|
case 3:
|
|
if(MSG.length>0){
|
|
out = bytesToStringUTFCustom(MSG);
|
|
}
|
|
else{
|
|
return null;
|
|
}
|
|
}
|
|
OPCOUNT++;
|
|
}
|
|
}
|
|
}catch(IOException e){
|
|
console.out("Socket Exception", "Client socket is closed?! Closing socket connection!");
|
|
close();
|
|
}
|
|
console.socket(id, "Message: "+out);
|
|
return out;
|
|
}
|
|
//
|
|
// public static Boolean checkProtocol(String input){
|
|
// JsonObject first = Json.createParser(new StringReader(input)).getStrint;
|
|
// }
|
|
|
|
// public void sControl(){
|
|
// try{
|
|
// while (true) {
|
|
// String input = reader.readLine();
|
|
// if(input == null){break;}
|
|
// Pattern main = Pattern.compile(";");
|
|
// if(!httpAuth) {
|
|
// if (!ctServer.checkClient(this, main.split(input))) {
|
|
// writer.println("CONNECT_DENIED");
|
|
// break;
|
|
// }
|
|
// }
|
|
// switch (input) {
|
|
// case "EXIT":
|
|
// socket.close();
|
|
// break;
|
|
// default:
|
|
// writer.println("Client info - " + socket.getRemoteSocketAddress().toString());
|
|
// console.out("Socket "+socketID+" Message",input);
|
|
// }
|
|
// }
|
|
// } catch (IOException e) {
|
|
// console.out("Server Error:", e.toString());
|
|
// } finally {
|
|
// close();
|
|
// try {
|
|
// socket.close();
|
|
// console.out("Server", "Socket "+socketID+" disconnected!");
|
|
// } catch (IOException e) {
|
|
// console.out("Server","System socket closing error!");
|
|
// }catch (NullPointerException e){
|
|
//
|
|
// }
|
|
//
|
|
// }
|
|
// }
|
|
|
|
}
|
|
|