Java 實現Http協議
HTTP協議屬于應用層協議,它構建于TCP和IP協議之上,處于TCP/IP協議架構層的頂端,所以,它不用處理下層協議間諸如丟包補發、握手及數據的分段及重新組裝等繁瑣的細節,使開發人員可以專注于應用業務。
協議是通信的規范,為了更好的理解HTTP協議,我們可以基于Java的Socket API接口,通過設計一個簡單的應用層通信協議,來簡單分析下協議實現的過程和細節。
在我們今天的示例程序中,客戶端會向服務端發送一條命令,服務端在接收到命令后,會判斷命令是否是“HELLO”,如果是“HELLO”, 則服務端返回給客戶端的響應為“hello”,否則,服務端返回給客戶端的響應為“bye bye”。
我們接下來用Java實現這個簡單的應用層通信協議,
一、協議請求的定義
協議的請求主要包括:編碼、命令和命令長度三個字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package com.binghe.params; /** * 協議請求的定義 * @author binghe * */ public class Request { /** * 協議編碼 */ private byte encode; /** * 命令 */ private String command; /** * 命令長度 */ private int commandLength; public Request() { super (); } public Request( byte encode, String command, int commandLength) { super (); this .encode = encode; this .command = command; this .commandLength = commandLength; } public byte getEncode() { return encode; } public void setEncode( byte encode) { this .encode = encode; } public String getCommand() { return command; } public void setCommand(String command) { this .command = command; } public int getCommandLength() { return commandLength; } public void setCommandLength( int commandLength) { this .commandLength = commandLength; } @Override public String toString() { return "Request [encode=" + encode + ", command=" + command + ", commandLength=" + commandLength + "]" ; } } |
二、響應協議的定義
協議的響應主要包括:編碼、響應內容和響應長度三個字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package com.binghe.params; /** * 協議響應的定義 * @author binghe * */ public class Response { /** * 編碼 */ private byte encode; /** * 響應內容 */ private String response; /** * 響應長度 */ private int responseLength; public Response() { super (); } public Response( byte encode, String response, int responseLength) { super (); this .encode = encode; this .response = response; this .responseLength = responseLength; } public byte getEncode() { return encode; } public void setEncode( byte encode) { this .encode = encode; } public String getResponse() { return response; } public void setResponse(String response) { this .response = response; } public int getResponseLength() { return responseLength; } public void setResponseLength( int responseLength) { this .responseLength = responseLength; } @Override public String toString() { return "Response [encode=" + encode + ", response=" + response + ", responseLength=" + responseLength + "]" ; } } |
三、編碼常量定義
編碼常量的定義主要包括UTF-8和GBK兩種編碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.binghe.constant; /** * 常量類 * @author binghe * */ public final class Encode { //UTF-8編碼 public static final byte UTF8 = 1 ; //GBK編碼 public static final byte GBK = 2 ; } |
四、客戶端的實現
客戶端先構造一個request請求,通過Socket接口將其發送到遠端,并接收遠端的響應信息,并構造成一個Response對象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.binghe.protocol.client; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import com.binghe.constant.Encode; import com.binghe.params.Request; import com.binghe.params.Response; import com.binghe.utils.ProtocolUtils; /** * 客戶端代碼 * @author binghe * */ public final class Client { public static void main(String[] args) throws IOException{ //請求 Request request = new Request(); request.setCommand( "HELLO" ); request.setCommandLength(request.getCommand().length()); request.setEncode(Encode.UTF8); Socket client = new Socket( "127.0.0.1" , 4567 ); OutputStream out = client.getOutputStream(); //發送請求 ProtocolUtils.writeRequest(out, request); //讀取響應數據 InputStream in = client.getInputStream(); Response response = ProtocolUtils.readResponse(in); System.out.println( "獲取的響應結果信息為: " + response.toString()); } } |
五、服務端的實現
服務端接收客戶端的請求,根據接收命令的不同,響應不同的消息信息,如果是“HELLO”命令,則響應“hello”信息,否則響應“bye bye”信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.binghe.protocol.server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import com.binghe.constant.Encode; import com.binghe.params.Request; import com.binghe.params.Response; import com.binghe.utils.ProtocolUtils; /** * Server端代碼 * @author binghe * */ public final class Server { public static void main(String[] args) throws IOException{ ServerSocket server = new ServerSocket( 4567 ); while ( true ) { Socket client = server.accept(); //讀取請求數據 InputStream input = client.getInputStream(); Request request = ProtocolUtils.readRequest(input); System.out.println( "收到的請求參數為: " + request.toString()); OutputStream out = client.getOutputStream(); //組裝響應數據 Response response = new Response(); response.setEncode(Encode.UTF8); if ( "HELLO" .equals(request.getCommand())){ response.setResponse( "hello" ); } else { response.setResponse( "bye bye" ); } response.setResponseLength(response.getResponse().length()); ProtocolUtils.writeResponse(out, response); } } } |
六、ProtocolUtils工具類的實現
ProtocolUtils
的readRequest
方法將從傳遞進來的輸入流中讀取請求的encode
、command
和commandLength
三個參數,進行相應的編碼轉化,構造成Request
對象返回。而writeResponse
方法則是將response
對象的字段根據對應的編碼寫入到響應的輸出流中。
有一個細節需要重點注意:OutputStream
中直接寫入一個int類型,會截取其低8位,丟棄其高24位,所以,在傳遞和接收數據時,需要進行相應的轉化操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
package com.binghe.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.binghe.constant.Encode; import com.binghe.params.Request; import com.binghe.params.Response; /** * 協議工具類 * @author binghe * */ public final class ProtocolUtils { /** * 從輸入流中反序列化Request對象 * @param input * @return * @throws IOException */ public static Request readRequest(InputStream input) throws IOException{ //讀取編碼 byte [] encodeByte = new byte [ 1 ]; input.read(encodeByte); byte encode = encodeByte[ 0 ]; //讀取命令長度 byte [] commandLengthBytes = new byte [ 4 ]; input.read(commandLengthBytes); int commandLength = ByteUtils.byte2Int(commandLengthBytes); //讀取命令 byte [] commandBytes = new byte [commandLength]; input.read(commandBytes); String command = "" ; if (Encode.UTF8 == encode){ command = new String(commandBytes, "UTF-8" ); } else if (Encode.GBK == encode){ command = new String(commandBytes, "GBK" ); } //組裝請求返回 Request request = new Request(encode, command, commandLength); return request; } /** * 從輸入流中反序列化Response對象 * @param input * @return * @throws IOException */ public static Response readResponse(InputStream input) throws IOException{ //讀取編碼 byte [] encodeByte = new byte [ 1 ]; input.read(encodeByte); byte encode = encodeByte[ 0 ]; //讀取響應長度 byte [] responseLengthBytes = new byte [ 4 ]; input.read(responseLengthBytes); int responseLength = ByteUtils.byte2Int(responseLengthBytes); //讀取命令 byte [] responseBytes = new byte [responseLength]; input.read(responseBytes); String response = "" ; if (Encode.UTF8 == encode){ response = new String(responseBytes, "UTF-8" ); } else if (Encode.GBK == encode){ response = new String(responseBytes, "GBK" ); } //組裝請求返回 Response resp = new Response(encode, response, responseLength); return resp; } /** * 序列化請求信息 * @param output * @param response */ public static void writeRequest(OutputStream output, Request request) throws IOException{ //將response響應返回給客戶端 output.write(request.getEncode()); //output.write(response.getResponseLength());直接write一個int類型會截取低8位傳輸丟棄高24位 output.write(ByteUtils.int2ByteArray(request.getCommandLength())); if (Encode.UTF8 == request.getEncode()){ output.write(request.getCommand().getBytes( "UTF-8" )); } else if (Encode.GBK == request.getEncode()){ output.write(request.getCommand().getBytes( "GBK" )); } output.flush(); } /** * 序列化響應信息 * @param output * @param response */ public static void writeResponse(OutputStream output, Response response) throws IOException{ //將response響應返回給客戶端 output.write(response.getEncode()); //output.write(response.getResponseLength());直接write一個int類型會截取低8位傳輸丟棄高24位 output.write(ByteUtils.int2ByteArray(response.getResponseLength())); if (Encode.UTF8 == response.getEncode()){ output.write(response.getResponse().getBytes( "UTF-8" )); } else if (Encode.GBK == response.getEncode()){ output.write(response.getResponse().getBytes( "GBK" )); } output.flush(); } } |
七、ByteUtils類的實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package com.binghe.utils; /** * 字節轉化工具類 * @author binghe * */ public final class ByteUtils { /** * 將byte數組轉化為int數字 * @param bytes * @return */ public static int byte2Int( byte [] bytes){ int num = bytes[ 3 ] & 0xFF ; num |= ((bytes[ 2 ] << 8 ) & 0xFF00 ); num |= ((bytes[ 1 ] << 16 ) & 0xFF0000 ); num |= ((bytes[ 0 ] << 24 ) & 0xFF000000 ); return num; } /** * 將int類型數字轉化為byte數組 * @param num * @return */ public static byte [] int2ByteArray( int i){ byte [] result = new byte [ 4 ]; result[ 0 ] = ( byte )(( i >> 24 ) & 0xFF ); result[ 1 ] = ( byte )(( i >> 16 ) & 0xFF ); result[ 2 ] = ( byte )(( i >> 8 ) & 0xFF ); result[ 3 ] = ( byte )(i & 0xFF ); return result; } } |
到此這篇關于關于Java 實現Http協議詳細內容的文章就介紹到這了,更多相關Java 實現Http協議內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/l1028386804/article/details/117154518