Java,InputStream,Socket阻塞.(關於HTTP請求的IO問題自我總結)
前言:
由於專案的需求,需要實現以下流程:
1. Client傳送HTTP請求到Server.
2. Server接收HTTP請求並顯示出請求的內容(包含請求的頭及Content的內容)
服務端實現:
Server部分程式碼如下:
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
/**
*
* @author Anson
*/
public class Server{
private String ServerIP;
private int ServerPort;
private boolean shutdown = false;
/**
* Init Server
*/
public void init(){
ServerIP = "192.168.0.1";
ServerPort = 8082;
}
/**
*
*/
public void await(){
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(ServerPort, 1, InetAddress.getByName(ServerIP));
}catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// Loop waiting for a request
while(!shutdown){
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
this.parse(input);
// Close the socket
socket.close();
}catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
public void parse(InputStream input) {
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
}catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j=0; j<i; j++) {
request.append((char) buffer[j]);
}
System.out.println(request.toString());
}
}
再編寫StartServer.java
....
public class StartServer{
public static void main(String[] args){
Server server = new Server();
server.init();
server.await();
.....
}
}
跟著,客戶端的程式碼做法有很多,
可以用最簡單的Socket,
也可以用HttpClient,
或其它客戶端如:OC
具體做法,網上有很多,在此不細述.
我用的是OC,
當然,可能有些做法,並不會出現這樣的問題,
畢竟,我,並非一個高手.
我只描述我遇到的狀況,
成功實現的可以忽略.
問題描述:
當客戶端傳送的資料量小於一定長度(如2048byte)的時候,
Server執行正常,即可以正常列印出請求的內容.
如:GET index HTTP/1.1
........
請求的頭加上一些Content.
但當資料量大於2048的時候,奇怪的問題出現了.
有時會發現資料讀不全.
例如,在請求的頭下面,Content的內容是一串XML的String:
<?xml version="1.0" encoding="UTF-8" ?><label1>label1></label1>......
當請求的頭加上XML的String的總長度超過2048,那麼,可能出現在情況就是超過的部分丟失.
解決辦法:
可能已經有人發覺,在上面的Server裡面的parse(InputStream input)這裡的處理有問題.
因為很大程度上這個2048在StringBuffer的定義時就出現了.
所以,第一個失敗的嘗試便是修改2048.
第一次變成10*2048
跟著是100*2048....
最後是1000*2048....
但結果可以發現:資料量的限制並不會隨著這個數值的成倍增長而以相同倍數增長.有時可能是為了增加一倍,
而這個數值必需增加20倍.....
最後發現這個辦法不可能,
於是,改變了讀取的辦法.
用了其它的如StreamBufferReader還有很多其它的來嘗試讀取,
結果卻令人失望,
甚至於這時候出現了阻塞.
即,在讀取的過停中,卡住了,
直到等到客戶端的請求超時,才跳出來.
所以這些方法也失敗了.
具體的情況也記不大清楚了.
之後的另一個方法:
String request = "";
//如果不先讀一次,那麼下面的input.available()有可能是0值.
char firstbyte = (char) input.read();
request += firstbyte;
int ava = 0;
while ((ava = input.available()) > 0) {
try {
// 在此睡眠0.1秒,很重要
Thread.sleep(100);
} catch (Exception t) {
t.printStackTrace();
}
byte[] bufferedbyte = new byte[ava];
input.read(bufferedbyte, 0, ava);
request += new String(bufferedbyte, "UTF-8");
}
這是一個成功解決的方法,
之所以要睡眠0.1秒,等待高手幫忙解答,
這也許跟網路有關係,
當然,資料量越大,睡眠的時應該有小小的加長.(緩衝時間).
雖然以上的做法成功實現了,但對於伺服器來說,效率是個問題,所以只能尋找更優的辦法.
第三次更改:
int readInt = 0;
int availableCount = 0;
char requestHead = (char)(input.read());
request += requestHead;
//取得請求的型別是PUT或GET
if(requestHead == 'P') {//主要是PUT的方法裡面會帶有XML.
while((readInt=input.read()) != -1) {
request += (char)readInt;
if(request.indexOf("</endXML>") != -1){
break;
}
}
System.out.println("this is put\n" +request);
} else if(requestHead=='G') {//GET的方法內容比較少,用以下的方法可以正常實現
while((availableCount=input.available()) > 0){
byte[] requestbuffer = new byte[availableCount];
input.read(requestbuffer);
request += new String(requestbuffer, "UTF-8");
}
*程式碼並不完整.
這種做法,我在讀取XML的String里加上了一個結束的判斷即對"</endXML>"的判斷(當然,這是在知道Content內容的基礎上這種做法才可行).
雖然暫時解決了問題,但仍然不能完美解決存在的問題.
第四次更改:
這也是最後一次更改,辦法是:
在PUT的請求裡面,先取得一個Content-Length:的請求頭的值length.
這裡的大小就是Content的長度.
在這個基礎上,
先判斷是否開始讀取Content:
if(requestString.indexOf("\r\n\r\n") != -1)
.....
之後再迴圈讀取:
for(int i=0; i < length; i++){
requestString += (char)input.read();
}
當讀完length後,跳出迴圈,
並跳出讀取input的讀取.
.......
問題在此告一段落.
若有哪位朋友懂得其中原理,懇請告知,
知其然而不知其所以然,心裡不免有個結.
結言:
以上做法,僅供參考.
若有錯誤,請不嗇指出.
歡迎轉載
轉載進請註明轉自:http://ansonlai.iteye.com
相關文章
- Python 使用socket模擬http請求,從阻塞到協程PythonHTTP
- Java IO: InputStreamJava
- 關於兩次http請求,後一次請求影響前一次請求的問題HTTP
- Http請求get與post請求方式的各種相關面試總結HTTP面試
- 關於在request請求時,處理請求引數的問題
- 關於socket.io的使用
- [前端 · 面試 ]HTTP 總結(三)—— HTTP 請求方法前端面試HTTP
- golang開發:http請求redirect的問題GolangHTTP
- 關於vue請求laravel介面跨域問題VueLaravel跨域
- 前端http請求跨域問題解決前端HTTP跨域
- SpringMVC——HTTP請求專案實踐整理總結SpringMVCHTTP
- TCP/IP、HTTP和Socket總結TCPHTTP
- Http請求相關(轉)HTTP
- java傳送http請求JavaHTTP
- Java實現Http請求JavaHTTP
- 使用Socket進行HTTP請求與報文講解HTTP
- JAVA設定http請求代理JavaHTTP
- 關於常用的http請求頭以及響應頭詳解HTTP
- 關於非同步AJAX請求執行速度過快問題非同步
- Java——IO總結2Java
- java同步非阻塞IOJava
- 大請求、請求超時問題
- java 偽造http請求ip地址JavaHTTP
- Java Http Get Post 請求工具類JavaHTTP
- Java 非阻塞 IO 和非同步 IOJava非同步
- 關於一個迴圈請求與迴圈計時器的問題
- 關於程式和執行緒 自我的一些總結執行緒
- URL請求不能解決中文請求的問題
- 多執行緒應用–Http請求阻塞回撥處理執行緒HTTP
- 多執行緒應用--Http請求阻塞回撥處理執行緒HTTP
- 阻塞IO與非阻塞IO
- 關於HTTP和HTTPS常見問題HTTP
- JAVA阻塞IO(BIO)簡介Java
- Vue 使用 Axios 傳送請求的請求體問題VueiOS
- 使用CloseableHttpClient 訪問 http 和https 的get請求HTTPclient
- mysql相關問題總結MySql
- http請求概述HTTP
- Jsoup http請求JSHTTP
- go http請求GoHTTP