Я работаю над приложением Java, который имеет встроенный HTTP-сервер, на данный момент сервер реализован с использованием ServerSocketChannel, он прослушивает порт 1694 для запросов:сервера HTTP Java отсылки фрагментированный ответа
msvrCh = ServerSocketChannel.open();
msvrCh.socket().bind(new InetSocketAddress(mintPort));
msvrCh.configureBlocking(false);
нить устанавливается для управления запросами и ответами:
Thread thrd = new Thread(msgReceiver);
thrd.setUncaughtExceptionHandler(exceptionHandler);
thrd.start();
нить довольно прост:
Runnable msgReceiver = new Runnable() {
@Override
public void run() {
try{
while(!Thread.interrupted()) {
//Sleep a short period between checks for new requests
try{
Thread.sleep(DELAY_BETWEEN_ACCEPTS);
} catch(Exception ex) {
ex.printStackTrace();
}
SocketChannel cliCh = msvrCh.accept();
if (blnExit() == true) {
break;
}
if (cliCh == null) {
continue;
}
processRequest(cliCh.socket());
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
logMsg(TERMINATING_THREAD +
"for accepting cluster connections", true);
if (msvrCh != null) {
try {
msvrCh.close();
} catch (IOException ex) {
ex.printStackTrace();
}
msvrCh = null;
}
}
}
};
Основная часть коды для работы с ответом в функции ProcessRequest:
private void processRequest(Socket sck) {
try {
//AJAX Parameters
final String AJAX_ID = "ajmid";
//The 'Handler Key' used to decode response
final String HANDLER_KEY = "hkey";
//Message payload
final String PAYLOAD = "payload";
//Post input buffer size
final int REQUEST_BUFFER_SIZE = 4096;
//Double carriage return marks the end of the headers
final String CRLF = "\r\n";
BufferedReader in = new BufferedReader(new InputStreamReader(sck.getInputStream()));
String strAMID = null, strHKey = null, strRequest;
char[] chrBuffer = new char[REQUEST_BUFFER_SIZE];
StringBuffer sbRequest = new StringBuffer();
eMsgTypes eType = eMsgTypes.UNKNOWN;
clsHTTPparameters objParams = null;
int intPos, intCount;
//Extract the entire request, including headers
if ((intCount = in.read(chrBuffer)) == 0) {
throw new Exception("Cannot read request!");
}
sbRequest.append(chrBuffer, 0, intCount);
strRequest = sbRequest.toString();
//What method is being used by this request?
if (strRequest.startsWith(HTTP_GET)) {
//The request should end with a HTTP marker, remove this before trying to interpret the data
if (strRequest.indexOf(HTTP_MARKER) != -1) {
strRequest = strRequest.substring(0, strRequest.indexOf(HTTP_MARKER)).trim();
}
//Look for a data marker
if ((intPos = strRequest.indexOf(HTTP_DATA_START)) >= 0) {
//Data is present in the query, skip to the start of the data
strRequest = strRequest.substring(intPos + 1);
} else {
//Remove the method indicator
strRequest = strRequest.substring(HTTP_GET.length());
}
} else if (strRequest.startsWith(HTTP_POST)) {
//Discard the headers and jump to the data
if ((intPos = strRequest.lastIndexOf(CRLF)) >= 0) {
strRequest = strRequest.substring(intPos + CRLF.length());
}
}
if (strRequest.length() > 1) {
//Extract the parameters
objParams = new clsHTTPparameters(strRequest);
}
if (strRequest.startsWith("/") == true) {
//Look for the document reference
strRequest = strRequest.substring(1);
eType = eMsgTypes.SEND_DOC;
}
if (objParams != null) {
//Transfer the payload to the request
String strPayload = objParams.getValue(PAYLOAD);
if (strPayload != null) {
byte[] arybytPayload = Base64.decodeBase64(strPayload.getBytes());
strRequest = new String(arybytPayload);
strAMID = objParams.getValue(AJAX_ID);
strHKey = objParams.getValue(HANDLER_KEY);
}
}
if (eType == eMsgTypes.UNKNOWN
&& strRequest.startsWith("{") && strRequest.endsWith("}")) {
//The payload is JSON, is there a type parameter?
String strType = strGetJSONItem(strRequest, JSON_LBL_TYPE);
if (strType != null && strType.length() > 0) {
//Decode the type
eType = eMsgTypes.valueOf(strType.toUpperCase().trim());
//What system is the message from?
String strIP = strGetJSONItem(strRequest, JSON_LBL_IP)
,strMAC = strGetJSONItem(strRequest, JSON_LBL_MAC);
if (strIP != null && strIP.length() > 0
&& strMAC != null && strMAC.length() > 0) {
//Is this system known in the cluster?
clsIPmon objSystem = objAddSysToCluster(strIP, strMAC);
if (objSystem != null) {
//Update the date/time stamp of the remote system
objSystem.touch();
}
//This is an internal cluster message, no response required
return;
}
}
}
String strContentType = null, strRespPayload = null;
OutputStream out = sck.getOutputStream();
byte[] arybytResponse = null;
boolean blnShutdown = false;
out.write("HTTP/1.0 200\n".getBytes());
switch(eType) {
case SEND_DOC:
if (strRequest.length() <= 1) {
strRequest = HTML_ROOT + DEFAULT_DOC;
} else {
strRequest = HTML_ROOT + strRequest;
}
logMsg("HTTP Request for: " + strRequest, true);
if (strRequest.toLowerCase().endsWith(".css") == true) {
strContentType = MIME_CSS;
} else if (strRequest.toLowerCase().endsWith(".gif") == true) {
strContentType = MIME_GIF;
} else if (strRequest.toLowerCase().endsWith(".jpg") == true) {
strContentType = MIME_JPG;
} else if (strRequest.toLowerCase().endsWith(".js") == true) {
strContentType = MIME_JS;
} else if (strRequest.toLowerCase().endsWith(".png") == true) {
strContentType = MIME_PNG;
} else if (strRequest.toLowerCase().endsWith(".html") == true
|| strRequest.toLowerCase().endsWith(".htm") == true) {
strContentType = MIME_HTML;
}
File objFile = new File(strRequest);
if (objFile.exists() == true) {
FileInputStream objFIS = new FileInputStream(objFile);
if (objFIS != null) {
arybytResponse = new byte[(int)objFile.length()];
if (objFIS.read(arybytResponse) == 0) {
arybytResponse = null;
}
objFIS.close();
}
}
break;
case CHANNEL_STS:
strRespPayload = strChannelStatus(strRequest);
strContentType = MIME_JSON;
break;
case CLUSTER_STS:
strRespPayload = strClusterStatus();
strContentType = MIME_JSON;
break;
case MODULE_STS:
strRespPayload = strModuleStatus(strRequest);
strContentType = MIME_JSON;
break;
case NETWORK_INF:
strRespPayload = strNetworkInfo(strRequest);
strContentType = MIME_JSON;
break;
case NODE_STS:
strRespPayload = strNodeStatus(strRequest);
strContentType = MIME_JSON;
break;
case POLL_STS:
strRespPayload = strPollStatus(strRequest);
strContentType = MIME_JSON;
break;
case SYS_STS:
//Issue system status
strRespPayload = strAppStatus();
strContentType = MIME_JSON;
break;
case SHUTDOWN:
//Issue instruction to restart system
strRespPayload = "Shutdown in progress!";
strContentType = MIME_PLAIN;
//Flag that shutdown has been requested
blnShutdown = true;
break;
default:
}
if (strRespPayload != null) {
//Convert response string to byte array
arybytResponse = strRespPayload.getBytes();
System.out.println("[ " + strRespPayload.length() + " ]: " + strRespPayload); //HACK
}
if (arybytResponse != null && arybytResponse.length > 0) {
if (strContentType == MIME_JSON) {
String strResponse = "{";
if (strAMID != null) {
//Include the request AJAX Message ID in the response
if (strResponse.length() > 1) {
strResponse += ",";
}
strResponse += "\"" + AJAX_ID + "\":" + strAMID;
}
if (strHKey != null) {
if (strResponse.length() > 1) {
strResponse += ",";
}
strResponse += "\"" + HANDLER_KEY + "\":\"" + strHKey + "\"";
}
if (strResponse.length() > 1) {
strResponse += ",";
}
strResponse += "\"payload\":" + new String(arybytResponse)
+ "}";
arybytResponse = strResponse.getBytes();
}
String strHeaders = "";
if (strContentType != null) {
strHeaders += "Content-type: " + strContentType + "\n";
}
strHeaders += "Content-length: " + arybytResponse.length + "\n"
+ "Access-Control-Allow-Origin: *\n"
+ "Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\n"
+ "Access-Control-Allow-Credentials: true\n"
+ "Keep-Alive: timeout=2, max=100\n"
+ "Cache-Control: no-cache\n"
+ "Pragma: no-cache\n\n";
out.write(strHeaders.getBytes());
out.write(arybytResponse);
out.flush();
}
out.close();
sck.close();
if (blnShutdown == true) {
String strSystem = mobjLocalIP.strGetIP();
if (strSystem.compareTo(mobjLocalIP.strGetIP()) != 0) {
//Specified system is not the local system, issue message to remote system.
broadcastMessage("{\"" + JSON_LBL_TYPE + "\":\"" +
eMsgTypes.SHUTDOWN + "\""
+ ",\"" + JSON_LBL_TIME + "\":\"" +
clsTimeMan.lngTimeNow() + "\"}");
} else {
//Shutdown addressed to local system
if (getOS().indexOf("linux") >= 0) {
//TO DO!!!
} else if (getOS().indexOf("win") >= 0) {
Runtime runtime = Runtime.getRuntime();
runtime.exec("shutdown /r /c \"Shutdown request\" /t 0 /f");
System.exit(EXITCODE_REQUESTED_SHUTDOWN);
}
}
}
} catch (Exception ex) {
} finally {
if (sck != null) {
try {
sck.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
Я хотел бы реализовал фрагментированный ответ, в настоящее время фрагментированных ответы не поддерживаются кодом выше.
[Редактировать] Я пытался реализовать Chunked реакцию путем добавления метода:
/**
* @param strData - The data to split into chunks
* @return A string array containing the chunks
*/
public static String[] arystrChunkData(String strData) {
int intChunks = (strData.length()/CHUNK_THRESHOLD_BYTESIZE) + 1;
String[] arystrChunks = new String[intChunks];
int intLength = strData.length(), intPos = 0;
for(int c=0; c<arystrChunks.length; c++) {
if (intPos < intLength) {
//Extract a chunk from the data
int intEnd = Math.min(intLength - 1, intPos + CHUNK_THRESHOLD_BYTESIZE);
arystrChunks[c] = strData.substring(intPos, intEnd);
}
//Advance data position to next chunk
intPos += CHUNK_THRESHOLD_BYTESIZE;
}
return arystrChunks;
}
Модифицированный ProcessRequest теперь выглядит следующим образом:
private void processRequest(Socket sck) {
try {
//AJAX Parameters
final String AJAX_ID = "ajmid";
//The 'Handler Key' used to decode response
final String HANDLER_KEY = "hkey";
//Message payload
final String PAYLOAD = "payload";
//Post input buffer size
final int REQUEST_BUFFER_SIZE = 4096;
//Double carriage return marks the end of the headers
final String CRLF = "\r\n";
BufferedReader in = new BufferedReader(new InputStreamReader(sck.getInputStream()));
String strAMID = null, strHKey = null, strRequest;
char[] chrBuffer = new char[REQUEST_BUFFER_SIZE];
StringBuffer sbRequest = new StringBuffer();
eMsgTypes eType = eMsgTypes.UNKNOWN;
clsHTTPparameters objParams = null;
int intPos, intCount;
//Extract the entire request, including headers
if ((intCount = in.read(chrBuffer)) == 0) {
throw new Exception("Cannot read request!");
}
sbRequest.append(chrBuffer, 0, intCount);
strRequest = sbRequest.toString();
//What method is being used by this request?
if (strRequest.startsWith(HTTP_GET)) {
//The request should end with a HTTP marker, remove this before trying to interpret the data
if (strRequest.indexOf(HTTP_MARKER) != -1) {
strRequest = strRequest.substring(0, strRequest.indexOf(HTTP_MARKER)).trim();
}
//Look for a data marker
if ((intPos = strRequest.indexOf(HTTP_DATA_START)) >= 0) {
//Data is present in the query, skip to the start of the data
strRequest = strRequest.substring(intPos + 1);
} else {
//Remove the method indicator
strRequest = strRequest.substring(HTTP_GET.length());
}
} else if (strRequest.startsWith(HTTP_POST)) {
//Discard the headers and jump to the data
if ((intPos = strRequest.lastIndexOf(CRLF)) >= 0) {
strRequest = strRequest.substring(intPos + CRLF.length());
}
}
if (strRequest.length() > 1) {
//Extract the parameters
objParams = new clsHTTPparameters(strRequest);
}
if (strRequest.startsWith("/") == true) {
//Look for the document reference
strRequest = strRequest.substring(1);
eType = eMsgTypes.SEND_DOC;
}
if (objParams != null) {
//Transfer the payload to the request
String strPayload = objParams.getValue(PAYLOAD);
if (strPayload != null) {
byte[] arybytPayload = Base64.decodeBase64(strPayload.getBytes());
strRequest = new String(arybytPayload);
strAMID = objParams.getValue(AJAX_ID);
strHKey = objParams.getValue(HANDLER_KEY);
}
}
if (eType == eMsgTypes.UNKNOWN
&& strRequest.startsWith("{") && strRequest.endsWith("}")) {
//The payload is JSON, is there a type parameter?
String strType = strGetJSONItem(strRequest, JSON_LBL_TYPE);
if (strType != null && strType.length() > 0) {
//Decode the type
eType = eMsgTypes.valueOf(strType.toUpperCase().trim());
//What system is the message from?
String strIP = strGetJSONItem(strRequest, JSON_LBL_IP)
,strMAC = strGetJSONItem(strRequest, JSON_LBL_MAC);
if (strIP != null && strIP.length() > 0
&& strMAC != null && strMAC.length() > 0) {
//Is this system known in the cluster?
clsIPmon objSystem = objAddSysToCluster(strIP, strMAC);
if (objSystem != null) {
//Update the date/time stamp of the remote system
objSystem.touch();
}
//This is an internal cluster message, no response required
return;
}
}
}
String strContentType = null, strRespPayload = null;
OutputStream out = sck.getOutputStream();
byte[] arybytResponse = null;
boolean blnShutdown = false;
//Start the writing the headers
String strHeaders = "HTTP/1.0 200\n"
+ "Date: " + (new Date()).toString() + "\n"
+ "Access-Control-Allow-Origin: *\n"
+ "Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\n"
+ "Access-Control-Allow-Credentials: true\n"
+ "Keep-Alive: timeout=2, max=100\n"
+ "Cache-Control: no-cache\n"
+ "Pragma: no-cache\n";
out.write(strHeaders.getBytes());
strHeaders = "";
switch(eType) {
case SEND_DOC:
if (strRequest.length() <= 1) {
strRequest = HTML_ROOT + DEFAULT_DOC;
} else {
strRequest = HTML_ROOT + strRequest;
}
logMsg("HTTP Request for: " + strRequest, true);
if (strRequest.toLowerCase().endsWith(".css") == true) {
strContentType = MIME_CSS;
} else if (strRequest.toLowerCase().endsWith(".gif") == true) {
strContentType = MIME_GIF;
} else if (strRequest.toLowerCase().endsWith(".jpg") == true) {
strContentType = MIME_JPG;
} else if (strRequest.toLowerCase().endsWith(".js") == true) {
strContentType = MIME_JS;
} else if (strRequest.toLowerCase().endsWith(".png") == true) {
strContentType = MIME_PNG;
} else if (strRequest.toLowerCase().endsWith(".html") == true
|| strRequest.toLowerCase().endsWith(".htm") == true) {
strContentType = MIME_HTML;
}
File objFile = new File(strRequest);
if (objFile.exists() == true) {
FileInputStream objFIS = new FileInputStream(objFile);
if (objFIS != null) {
arybytResponse = new byte[(int)objFile.length()];
if (objFIS.read(arybytResponse) == 0) {
arybytResponse = null;
}
objFIS.close();
}
}
break;
case CHANNEL_STS:
strRespPayload = strChannelStatus(strRequest);
strContentType = MIME_JSON;
break;
case CLUSTER_STS:
strRespPayload = strClusterStatus();
strContentType = MIME_JSON;
break;
case MODULE_STS:
strRespPayload = strModuleStatus(strRequest);
strContentType = MIME_JSON;
break;
case NETWORK_INF:
strRespPayload = strNetworkInfo(strRequest);
strContentType = MIME_JSON;
break;
case NODE_STS:
strRespPayload = strNodeStatus(strRequest);
strContentType = MIME_JSON;
break;
case POLL_STS:
strRespPayload = strPollStatus(strRequest);
strContentType = MIME_JSON;
break;
case SYS_STS:
//Issue system status
strRespPayload = strAppStatus();
strContentType = MIME_JSON;
break;
case SHUTDOWN:
//Issue instruction to restart system
strRespPayload = "Shutdown in progress!";
strContentType = MIME_PLAIN;
//Flag that shutdown has been requested
blnShutdown = true;
break;
default:
}
if (strRespPayload != null) {
//Convert response string to byte array
arybytResponse = strRespPayload.getBytes();
}
if (arybytResponse != null && arybytResponse.length > 0) {
boolean blnChunked = false;
if (strContentType != null) {
strHeaders += "Content-type: " + strContentType + "\n";
}
if (strContentType == MIME_JSON) {
String strResponse = "{";
if (strAMID != null) {
//Include the request AJAX Message ID in the response
if (strResponse.length() > 1) {
strResponse += ",";
}
strResponse += "\"" + AJAX_ID + "\":" + strAMID;
}
if (strHKey != null) {
if (strResponse.length() > 1) {
strResponse += ",";
}
strResponse += "\"" + HANDLER_KEY + "\":\"" + strHKey + "\"";
}
if (strResponse.length() > 1) {
strResponse += ",";
}
strResponse += "\"payload\":" + new String(arybytResponse)
+ "}";
//How big is the response?
if (strResponse.length() > CHUNK_THRESHOLD_BYTESIZE) {
blnChunked = true;
strHeaders += "Transfer-Encoding: chunked\n\n";
out.write(strHeaders.getBytes());
//Slice up the string into chunks
String[] arystrChunks = arystrChunkData(strResponse);
for(int c=0; c<arystrChunks.length; c++) {
String strChunk = arystrChunks[c];
if (strChunk != null) {
String strLength = Integer.toHexString(strChunk.length()) + "\r\n";
strChunk += "\r\n";
out.write(strLength.getBytes());
out.write(strChunk.getBytes());
}
}
//Last chunk is always 0 bytes
out.write("0\r\n\r\n".getBytes());
} else {
arybytResponse = strResponse.getBytes();
}
}
if (blnChunked == false) {
strHeaders += "Content-length: " + arybytResponse.length + "\n\n";
out.write(strHeaders.getBytes());
out.write(arybytResponse);
}
out.flush();
}
out.close();
sck.close();
if (blnShutdown == true) {
String strSystem = mobjLocalIP.strGetIP();
if (strSystem.compareTo(mobjLocalIP.strGetIP()) != 0) {
//Specified system is not the local system, issue message to remote system.
broadcastMessage("{\"" + JSON_LBL_TYPE + "\":\"" +
eMsgTypes.SHUTDOWN + "\""
+ ",\"" + JSON_LBL_TIME + "\":\"" +
clsTimeMan.lngTimeNow() + "\"}");
} else {
//Shutdown addressed to local system
if (getOS().indexOf("linux") >= 0) {
//TO DO!!!
} else if (getOS().indexOf("win") >= 0) {
Runtime runtime = Runtime.getRuntime();
runtime.exec("shutdown /r /c \"Shutdown request\" /t 0 /f");
System.exit(EXITCODE_REQUESTED_SHUTDOWN);
}
}
}
} catch (Exception ex) {
} finally {
if (sck != null) {
try {
sck.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
Я прочитал несколько спецификаций Отмеченные ответы, и насколько я могу судить, я отправляю данные в правильном формате, однако я ничего не получаю в браузере.
Возможно, я ошибочно полагаю, что браузер правильно скомпоновал бы куски в один, но я мог ошибаться. Обработчик клиентской стороны выглядит так:
this.responseHandler = function() {
try {
if (mobjHTTP == null
|| !(mobjHTTP.readyState == 4 && mobjHTTP.status == 200)
|| !(mstrResponseText = mobjHTTP.responseText)
|| mstrResponseText.length == 0) {
//Not ready or no response to decode
return;
}
//Do something with the response
} catch(ex) {
T.error("responseHandler:", ex);
}
};
Этот обработчик настройки в другом месте в объекте:
mobjHTTP.onreadystatechange = this.responseHandler;
Зачем вам нужен ответ? TCP делает это уже –
Я создаю ответ JSON, который может быть довольно большим> 4K. Некоторые браузеры, такие как IE, будут терпеть большие пакеты. Я хочу, чтобы решение работало для всех, сохраняя максимальный размер пакета до 768 байт на кусок. – SPlatten
Я уверен, что у вас не будет проблем в каком-либо популярном браузере, существует ли ограничение на длину URL-адреса GET, но на ответ? Вы уверены, что у вас есть проблема? –