HTTP協(xié)議包讀取過(guò)程的Java實(shí)現(xiàn)
之前的兩篇文章中《GET實(shí)現(xiàn)HTTP協(xié)議請(qǐng)求包的分析》和《HTTP協(xié)議請(qǐng)求包的Java實(shí)現(xiàn)》對(duì)HTTP協(xié)議包的請(qǐng)求連接內(nèi)容進(jìn)行了不少的講解。那么本文將繼續(xù)做一下補(bǔ)充,主要是講解如何完成讀取HTTP包。
以下我自己設(shè)計(jì)的一個(gè)讀取HTTP協(xié)議包的類(lèi)SocketRequest。
- public class SocketRequest
- {//從指定的Socket的InputStream中讀取數(shù)據(jù)
- private InputStreaminput;
- private Stringuri;
- private StringBufferrequest=new StringBuffer();//用于保存所有內(nèi)容
- private intCONTENT_LENGTH=0;//實(shí)際包內(nèi)容數(shù)據(jù)長(zhǎng)
- private boolean bePost = false;
- private boolean beHttpResponse = false;
- private boolean beChucked = false;
- private boolean beGet = false;
- private bytecrlf13 = (byte)13; //'r'
- private bytecrlf10 = (byte)10;//'n'
- public SocketRequest(InputStream input) {
- this.input = input;}
- public SocketRequest(Socket socket) {
- this.input = socket.getInputStream();}
- public void ReadData()
- {//解析 獲得InputStream的數(shù)據(jù)
- ReadHeader();//頭部
- if(beChucked) //為Chucked
- {int ChuckSize=0;
- while((ChuckSize=getChuckSize())>0) //多個(gè)Chucked
- {readLenData(ChuckSize+2);//讀取定長(zhǎng)數(shù)據(jù)}
- readLenData(2); //最后的2位}
- if(CONTENT_LENGTH>0)
- {readLenData(CONTENT_LENGTH);//讀取定長(zhǎng)數(shù)據(jù)}
- uri = "";//parseUri(new String(request));}
- private void readLenData(int size)//讀取定長(zhǎng)數(shù)據(jù)
- {int readed=0;//已經(jīng)讀取數(shù)
- try{
- int available=0;//input.available(); //可讀數(shù)
- if(available>(size-readed)) available=size-readed;
- while( readed<size )
- {while(available==0){//等到有數(shù)據(jù)可讀
- available = input.available(); //可讀數(shù)}
- if(available>(size-readed)) available= size-readed; //size-readed--剩余數(shù)
- if(available>2048) available= 2048; //size-readed--剩余數(shù)
- byte[] buffer = new byte[available];
- int reading = input.read(buffer);
- request=request.append(new String(buffer,0,reading));//byte數(shù)組相加
- readed+=reading;//已讀字符
- }}catch(IOException e){System.out.println("Read readLenData Error!");} }
- private voidReadHeader() //讀取頭部 并獲得大小
- {byte[]crlf= new byte[1];
- intcrlfNum= 0;//已經(jīng)連接的回車(chē)換行數(shù) crlfNum=4為頭部結(jié)束
- try{while( input.read(crlf)!=-1 )//讀取頭部
- {if(crlf[0]==crlf13 || crlf[0]==crlf10)
- {crlfNum++; }
- else
- {crlfNum=0;} //不是則清
- request=request.append(new String(crlf,0,1));//byte數(shù)組相加
- if(crlfNum==4) break;}}
- catch(IOException e){System.out.println("Read Http Header Error!");
- return;}
- String tempStr=(new String(request)).toUpperCase();//這里我只處理了GET與POST方法
- StringstrMethod= tempStr.substring(0,4);
- if(strMethod.equals("GET ")) //前
- {beGet=true;}
- else if(strMethod.equals("POST"))
- {bePost=true;
- getContentlen_Chucked(tempStr);}
- else {System.out.println("不支持的HTTP協(xié)議包類(lèi)型");}//其它的其它類(lèi)型 暫不支持
- }
- private void getContentlen_Chucked(String tempStr)//獲得長(zhǎng)度 CONTENT-LENGTH 或 是否為CHUNKED型
- {String ss1="CONTENT-LENGTH:";
- String ss2=new String("TRANSFER-ENCODING: CHUNKED");
- int clIndex= tempStr.indexOf(ss1);
- int chuckIndex = tempStr.indexOf(ss2);//為CHUNKED型
- byte requst[]= tempStr.getBytes();
- if(clIndex!=-1)
- { //從clIndex+1起至rn
- StringBuffer sb=new StringBuffer();
- for(int i=(clIndex+16);;i++)
- {if(requst[i]!=(byte)13 && requst[i]!=(byte)10 )
- {sb.append((char)requst[i]);}
- else
- break;}
- CONTENT_LENGTH=Integer.parseInt(sb.toString());//正式的HTML文件的大小
- //System.out.println("CONTENT_LENGTH=="+CONTENT_LENGTH);}
- if(chuckIndex!=-1) beChucked=true;}
- private intgetChuckSize() //Chuck大小{
- byte[]crlf= new byte[1];
- StringBuffersb1= new StringBuffer();
- intcrlfNum= 0;//已經(jīng)連接的回車(chē)換行數(shù) crlfNum=4為頭部結(jié)束
- try{while(input.read(crlf)!=-1)//讀取頭部{
- if(crlf[0]==crlf13 || crlf[0]==crlf10)
- {crlfNum++; }
- else
- {crlfNum=0;} //不是則清
- sb1.append((char)crlf[0]);
- request=request.append(new String(crlf,0,1));//byte數(shù)組相加
- if(crlfNum==2) break;}
- }catch(IOException e){
- System.out.println("Read Http Package Error!");
- return 0;}
- return Integer.parseInt((sb1.toString()).trim(),16); //16進(jìn)控制
- }//通過(guò)此來(lái)進(jìn)行過(guò)濾,是否為發(fā)至目標(biāo)服務(wù)器的HTTP協(xié)議包
- private String parseUri(String requestString) {
- int index1, index2;
- index1 = requestString.indexOf(' ');
- if (index1 != -1) {
- index2 = requestString.indexOf(' ', index1 + 1);
- if (index2 > index1)
- return requestString.substring(index1 + 1, index2);}
- return null;}
- public String getData() {
- return request.toString();}}
使用此類(lèi):
- SocketRequest request = new SocketRequest(socket); //socket為ServerSocket.accept()返回的Socket實(shí)例
- request.ReadData();//讀取數(shù)據(jù)
- request.getData();
為什么我要用這么大的力量去讀取呢,尤其是在因?yàn)镾ocket連接在發(fā)送數(shù)據(jù)時(shí),由于網(wǎng)絡(luò)的原因經(jīng)常會(huì)發(fā)生延遲現(xiàn)象,可能在服務(wù)器端開(kāi)始接收數(shù)據(jù)時(shí)可能只有部分?jǐn)?shù)據(jù)可以從InputStream中獲得,在一些地方處理不當(dāng)時(shí),可能只能獲得不完整的數(shù)據(jù)或是錯(cuò)誤的數(shù)據(jù)。
從InputStream讀取字節(jié)時(shí)有多種辦法:
常用int read()與int read(byte[] b)。在用read(byte[])時(shí),程序員經(jīng)常會(huì)犯錯(cuò)誤,因?yàn)樵诰W(wǎng)絡(luò)環(huán)境中,讀取的數(shù)據(jù)量不一定等于參數(shù)的大小。