一、Socket的概念
Socket是一種通訊機制,通常稱為套接字。英文原意是插座,顧明思義,Socket像是一個多孔插座,可以提供多個端口的連接服務
ps:至于socket在計算機術語中怎么就翻譯成了“套接字”這個令人費解的詞,這真是未解之謎。
二、Java Socket編程示例
2.1、基于TCP協議
tcp協議是面向連接的,通常會有服務端和客戶端,服務端和客戶端先連接,然后傳遞消息。
SendMsg:用于創建發送消息的線程
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;/*** 發送消息* @author cc**/
public class SendMsg implements Runnable {private OutputStream os;public SendMsg(OutputStream os) {super();this.os = os;}public OutputStream getOs() {return os;}public void setOs(OutputStream os) {this.os = os;}@Overridepublic void run() {BufferedReader consoleBr = new BufferedReader(new InputStreamReader(System.in));PrintWriter pw = new PrintWriter(os);String msg = null;while (true) {try {msg = consoleBr.readLine();pw.println(msg);pw.flush();} catch (IOException e) {e.printStackTrace();}}}}
RecevieMsg:用于創建接收消息的線程
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;/*** 接收消息* * @author cc**/
public class RecevieMsg implements Runnable {private InputStream is;public RecevieMsg(InputStream is) {super();this.is = is;}public InputStream getIs() {return is;}public void setIs(InputStream is) {this.is = is;}@Overridepublic void run() {BufferedReader netBr = new BufferedReader(new InputStreamReader(is));String msg = null;while (true) {try {msg = netBr.readLine();System.out.println(Thread.currentThread().getName() + "讀到一行數據:" + msg);} catch (IOException e) {e.printStackTrace();System.exit(0);}}}
}
Server:服務端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*** 服務端* @author cc**/
public class Server {public static void main(String[] args) {ServerSocket socket;try {socket = new ServerSocket(30000);System.out.println("我已經開啟服務了!");Socket client = socket.accept();System.out.println("有客戶端連接進來!");InputStream is = client.getInputStream();OutputStream os = client.getOutputStream();Thread thread = new Thread(new RecevieMsg(is), "服務端接收線程");Thread thread2 = new Thread(new SendMsg(os), "服務端發送線程");thread.start();thread2.start();} catch (IOException e) {e.printStackTrace();}}
}
Client:客戶端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*** 客戶端* @author cc**/
public class Client {public static void main(String[] args) {Socket socket;try {socket = new Socket("127.0.0.1", 30000);InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream();Thread thread = new Thread(new RecevieMsg(is), "客戶端接收線程");Thread thread2 = new Thread(new SendMsg(os), "客戶端發送線程");thread.start();thread2.start();} catch (IOException e) {e.printStackTrace();}}
}
啟動Server后再啟動Client,然后就可以通過兩個控制臺聊天啦!
擴展:用上面的代碼,如果是一個服務端和一個客戶端的話通過控制臺進行收發消息沒有問題,但是一個服務端和多個客戶端(Client類執行多次)的話會有問題,理由是:當你用服務端的終端發送消息的時候,這個消息應該發給哪個客戶端呢?實際情況是當服務端終端發送消息的條數達到客戶端的數量時,數據才會發出去,并且第一條對應的發給第一個客戶端。
因此我們不同通過服務端終端來給客戶端發送消息,更改了Server類并且新增了ServerHandleClientMsg類
Server
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*** 服務端* @author cc**/
public class Server {public static void main(String[] args) {try {ServerSocket socket = new ServerSocket(30000);System.out.println("我已經開啟服務了!");while(true){Socket client = socket.accept();System.out.println("有客戶端連接進來!");InputStream is = client.getInputStream();OutputStream os = client.getOutputStream();Thread thread = new Thread(new ServerHandleClientMsg(is,os), "服務端處理客戶端信息");thread.start();}} catch (IOException e) {e.printStackTrace();}}
}
ServerHandleClientMsg
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;/*** 服務端處理客戶端信息* @author cc**/
public class ServerHandleClientMsg implements Runnable{private InputStream is;private OutputStream os;public void setInputStream(InputStream inputStream) {this.is = inputStream;}public void setOutputStream(OutputStream outputStream) {this.os = outputStream;}public ServerHandleClientMsg(InputStream inputStream, OutputStream outputStream) {super();this.is = inputStream;this.os = outputStream;}boolean endFlag = false;@Overridepublic void run() {BufferedReader netBr = new BufferedReader(new InputStreamReader(is));String msg = null;while (true) {try {msg = netBr.readLine();System.out.println("線程名為"+Thread.currentThread().getName()+"、線程ID為"+Thread.currentThread().getId()+"的線程讀到一行數據:"+msg);PrintWriter pw = new PrintWriter(os);pw.println("服務端已收到您的消息:"+msg);pw.flush();} catch (IOException e) {e.printStackTrace();endFlag = true;}if(endFlag){break;}}}}
2.2、基于UDP協議
udp協議是無連接的,并且是不可靠的,直接向網絡發送數據報。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;public class Receive {public static void main(String[] args) {try {// 確定接受方的IP和端口號,IP地址為本地機器地址InetAddress ip = InetAddress.getLocalHost();int port = 8888;// 創建接收方的套接字,并指定端口號和IP地址DatagramSocket getSocket = new DatagramSocket(port, ip);// 確定數據報接受的數據的數組大小byte[] buf = new byte[1024];// 創建接受類型的數據報,數據將存儲在buf中DatagramPacket getPacket = new DatagramPacket(buf, buf.length);// 通過套接字接收數據getSocket.receive(getPacket);// 解析發送方傳遞的消息,并打印String getMes = new String(buf, 0, getPacket.getLength());System.out.println("對方發送的消息:" + getMes);// 通過數據報得到發送方的IP和端口號,并打印InetAddress sendIP = getPacket.getAddress();int sendPort = getPacket.getPort();System.out.println("對方的IP地址是:" + sendIP.getHostAddress());System.out.println("對方的端口號是:" + sendPort);// 通過數據報得到發送方的套接字地址SocketAddress sendAddress = getPacket.getSocketAddress();// 確定要反饋發送方的消息內容,并轉換為字節數組String feedback = "接收方說:我收到了!";byte[] backBuf = feedback.getBytes();// 創建發送類型的數據報DatagramPacket sendPacket = new DatagramPacket(backBuf,backBuf.length, sendAddress);// 通過套接字發送數據getSocket.send(sendPacket);// 關閉套接字getSocket.close();} catch (Exception e) {e.printStackTrace();}}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;public class Sender {public static void main(String[] args) {try {// 創建發送方的套接字,IP默認為本地,端口號隨機DatagramSocket sendSocket = new DatagramSocket();// 確定要發送的消息:String mes = "你好!接收方!";// 數據報的數據是以字節數組的形式存儲的byte[] buf = mes.getBytes();// 確定發送方的IP地址及端口號,地址為本地機器地址int port = 8888;InetAddress ip = InetAddress.getLocalHost();// 創建發送類型的數據報:DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, ip, port);// 通過套接字發送數據:sendSocket.send(sendPacket);// 確定接受反饋數據的緩沖存儲器,即存儲數據的字節數組byte[] getBuf = new byte[1024];// 創建接受類型的數據報DatagramPacket getPacket = new DatagramPacket(getBuf, getBuf.length);// 通過套接字接受數據sendSocket.receive(getPacket);// 解析反饋的消息,并打印String backMes = new String(getBuf, 0, getPacket.getLength());System.out.println("接受方返回的消息:" + backMes);// 關閉套接字sendSocket.close();} catch (Exception e) {e.printStackTrace();}}
}
先啟動Receive,在啟動Sender
上面的例子是點對點通信,下面我們來看如何進行多播
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket; public class MulticastListener { private int port; private String host; public MulticastListener(String host, int port) { this.host = host; this.port = port; } public void listen() { byte[] data = new byte[256]; try { InetAddress ip = InetAddress.getByName(this.host); MulticastSocket ms = new MulticastSocket(this.port); ms.joinGroup(ip); DatagramPacket packet = new DatagramPacket(data, data.length); //receive()是阻塞方法,會等待客戶端發送過來的信息 ms.receive(packet); String message = new String(packet.getData(), 0, packet.getLength()); System.out.println(message); ms.close(); } catch (Exception e) { e.printStackTrace(); System.exit(0);} } public static void main(String[] args) { int port = 1234; String host = "228.0.0.1"; MulticastListener ml = new MulticastListener(host, port); while (true) { ml.listen(); } }
}
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket; public class MulticastSender { private int port; private String host; private String data; public MulticastSender(String data, String host, int port) { this.data = data; this.host = host; this.port = port; } public void send() { try { InetAddress ip = InetAddress.getByName(this.host); DatagramPacket packet = new DatagramPacket(this.data.getBytes(), this.data.length(), ip, this.port); MulticastSocket ms = new MulticastSocket(); ms.send(packet); ms.close(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { int port = 1234; String host = "228.0.0.1"; String data = "hello world."; MulticastSender ms = new MulticastSender(data, host, port); ms.send(); }
}
先啟動MulticastListener,再啟動MulticastSender
ps:多播地址范圍224.0.0.0~239.255.255.255
2.3、基于HTTP協議
其實HTTP協議是TCP的一種。下面介紹了Java中基于HTTP協議的兩種通信方式。
2.3.1 URLConnection
JDK提供的基于HTTP協議的api實現
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;public class URLDemo {public static void main(String[] args) {try {URL url = new URL("https://www.baidu.com/");URLConnection urlConnection = url.openConnection();InputStream is = urlConnection.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String msg = null;while ((msg = br.readLine()) != null) {System.out.println(msg);}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}
2.3.2?HttpClient
除此之外HttpClient是Java中另一種基于Http協議的通信方式,相比JDK自帶的URLConnection,增加了易用性和靈活性。
下面給出了HttpClient的簡單Demo。環境:JDK1.8,用Maven構建,使用SpringBoot框架。
依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.cc</groupId><artifactId>HttpClient</artifactId><version>0.0.1-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.4.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- springboot的web和test啟動庫 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!-- apache httpclient組件 --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
應用啟動類
package com;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
Controller
package com.cc.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** Description: get和post請求測試controller* * @author JourWon* @date Created on 2018年4月19日*/
@RestController
@RequestMapping("/hello")
public class HelloWorldController {@GetMapping("/get")public String get() throws InterruptedException {return "get無參請求成功";}@GetMapping("/getWithParam")public String getWithParam(@RequestParam String message) {return "get帶參請求成功,參數message: " + message;}@PostMapping("/post")public String post(@RequestHeader("User-Agent") String userAgent, @RequestHeader("Accept") String accept,@RequestHeader("Accept-Language") String acceptLanguage,@RequestHeader("Accept-Encoding") String acceptEncoding, @RequestHeader("Cookie") String cookie,@RequestHeader("Connection") String conn) {// 打印請求頭信息System.out.println("Cookie = " + cookie);System.out.println("Connection = " + conn);System.out.println("Accept = " + accept);System.out.println("Accept-Language = " + acceptLanguage);System.out.println("Accept-Encoding = " + acceptEncoding);System.out.println("User-Agent = " + userAgent);return "post無參請求成功";}@PostMapping("/postWithParam")public String postWithParam(@RequestParam String code, @RequestParam String message) {return "post帶參請求成功,參數code: " + code + ",參數message: " + message;}}
HttpClient響應結果
package com.cc.util;
import java.io.Serializable;/*** Description: 封裝httpClient響應結果* * @author JourWon* @date Created on 2018年4月19日*/
public class HttpClientResult implements Serializable {/*** 響應狀態碼*/private int code;/*** 響應數據*/private String content;public HttpClientResult(int code, String content) {super();this.code = code;this.content = content;}public HttpClientResult(int code) {super();this.code = code;}public HttpClientResult() {super();}}
核心代碼:使用httpclient api發送http請求
package com.cc.util;import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;/*** Description: httpClient工具類* * @author JourWon* @date Created on 2018年4月19日*/
public class HttpClientUtils {// 編碼格式。發送編碼格式統一用UTF-8private static final String ENCODING = "UTF-8";// 設置連接超時時間,單位毫秒。private static final int CONNECT_TIMEOUT = 6000;// 請求獲取數據的超時時間(即響應時間),單位毫秒。private static final int SOCKET_TIMEOUT = 6000;/*** 發送get請求;不帶請求頭和請求參數* * @param url* 請求地址* @return* @throws Exception*/public static HttpClientResult doGet(String url) throws Exception {return doGet(url, null, null);}/*** 發送get請求;帶請求參數* * @param url* 請求地址* @param params* 請求參數集合* @return* @throws Exception*/public static HttpClientResult doGet(String url, Map<String, String> params) throws Exception {return doGet(url, null, params);}/*** 發送get請求;帶請求頭和請求參數* * @param url* 請求地址* @param headers* 請求頭集合* @param params* 請求參數集合* @return* @throws Exception*/public static HttpClientResult doGet(String url, Map<String, String> headers, Map<String, String> params)throws Exception {// 創建httpClient對象CloseableHttpClient httpClient = HttpClients.createDefault();// 創建訪問的地址URIBuilder uriBuilder = new URIBuilder(url);if (params != null) {Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {uriBuilder.setParameter(entry.getKey(), entry.getValue());}}// 創建http對象HttpGet httpGet = new HttpGet(uriBuilder.build());/*** setConnectTimeout:設置連接超時時間,單位毫秒。* setConnectionRequestTimeout:設置從connect Manager(連接池)獲取Connection* 超時時間,單位毫秒。這個屬性是新加的屬性,因為目前版本是可以共享連接池的。* setSocketTimeout:請求獲取數據的超時時間(即響應時間),單位毫秒。* 如果訪問一個接口,多少時間內無法返回數據,就直接放棄此次調用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpGet.setConfig(requestConfig);// 設置請求頭packageHeader(headers, httpGet);// 創建httpResponse對象CloseableHttpResponse httpResponse = null;try {// 執行請求并獲得響應結果return getHttpClientResult(httpResponse, httpClient, httpGet);} finally {// 釋放資源release(httpResponse, httpClient);}}/*** 發送post請求;不帶請求頭和請求參數* * @param url* 請求地址* @return* @throws Exception*/public static HttpClientResult doPost(String url) throws Exception {return doPost(url, null, null);}/*** 發送post請求;帶請求參數* * @param url* 請求地址* @param params* 參數集合* @return* @throws Exception*/public static HttpClientResult doPost(String url, Map<String, String> params) throws Exception {return doPost(url, null, params);}/*** 發送post請求;帶請求頭和請求參數* * @param url* 請求地址* @param headers* 請求頭集合* @param params* 請求參數集合* @return* @throws Exception*/public static HttpClientResult doPost(String url, Map<String, String> headers, Map<String, String> params)throws Exception {// 創建httpClient對象CloseableHttpClient httpClient = HttpClients.createDefault();// 創建http對象HttpPost httpPost = new HttpPost(url);/*** setConnectTimeout:設置連接超時時間,單位毫秒。* setConnectionRequestTimeout:設置從connect Manager(連接池)獲取Connection* 超時時間,單位毫秒。這個屬性是新加的屬性,因為目前版本是可以共享連接池的。* setSocketTimeout:請求獲取數據的超時時間(即響應時間),單位毫秒。* 如果訪問一個接口,多少時間內無法返回數據,就直接放棄此次調用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpPost.setConfig(requestConfig);// 設置請求頭/** httpPost.setHeader("Cookie", ""); httpPost.setHeader("Connection",* "keep-alive"); httpPost.setHeader("Accept", "application/json");* httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.9");* httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");* httpPost.setHeader("User-Agent",* "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"* );*/packageHeader(headers, httpPost);// 封裝請求參數packageParam(params, httpPost);// 創建httpResponse對象CloseableHttpResponse httpResponse = null;try {// 執行請求并獲得響應結果return getHttpClientResult(httpResponse, httpClient, httpPost);} finally {// 釋放資源release(httpResponse, httpClient);}}/*** 發送put請求;不帶請求參數* * @param url* 請求地址* @param params* 參數集合* @return* @throws Exception*/public static HttpClientResult doPut(String url) throws Exception {return doPut(url);}/*** 發送put請求;帶請求參數* * @param url* 請求地址* @param params* 參數集合* @return* @throws Exception*/public static HttpClientResult doPut(String url, Map<String, String> params) throws Exception {CloseableHttpClient httpClient = HttpClients.createDefault();HttpPut httpPut = new HttpPut(url);RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpPut.setConfig(requestConfig);packageParam(params, httpPut);CloseableHttpResponse httpResponse = null;try {return getHttpClientResult(httpResponse, httpClient, httpPut);} finally {release(httpResponse, httpClient);}}/*** 發送delete請求;不帶請求參數* * @param url* 請求地址* @param params* 參數集合* @return* @throws Exception*/public static HttpClientResult doDelete(String url) throws Exception {CloseableHttpClient httpClient = HttpClients.createDefault();HttpDelete httpDelete = new HttpDelete(url);RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpDelete.setConfig(requestConfig);CloseableHttpResponse httpResponse = null;try {return getHttpClientResult(httpResponse, httpClient, httpDelete);} finally {release(httpResponse, httpClient);}}/*** 發送delete請求;帶請求參數* * @param url* 請求地址* @param params* 參數集合* @return* @throws Exception*/public static HttpClientResult doDelete(String url, Map<String, String> params) throws Exception {if (params == null) {params = new HashMap<String, String>();}params.put("_method", "delete");return doPost(url, params);}/*** Description: 封裝請求頭* * @param params* @param httpMethod*/public static void packageHeader(Map<String, String> params, HttpRequestBase httpMethod) {// 封裝請求頭if (params != null) {Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {// 設置到請求頭到HttpRequestBase對象中httpMethod.setHeader(entry.getKey(), entry.getValue());}}}/*** Description: 封裝請求參數* * @param params* @param httpMethod* @throws UnsupportedEncodingException*/public static void packageParam(Map<String, String> params, HttpEntityEnclosingRequestBase httpMethod)throws UnsupportedEncodingException {// 封裝請求參數if (params != null) {List<NameValuePair> nvps = new ArrayList<NameValuePair>();Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}// 設置到請求的http對象中httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));}}/*** Description: 獲得響應結果* * @param httpResponse* @param httpClient* @param httpMethod* @return* @throws Exception*/public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception {// 執行請求httpResponse = httpClient.execute(httpMethod);// 獲取返回結果if (httpResponse != null && httpResponse.getStatusLine() != null) {String content = "";if (httpResponse.getEntity() != null) {content = EntityUtils.toString(httpResponse.getEntity(), ENCODING);}return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content);}return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);}/*** Description: 釋放資源* * @param httpResponse* @param httpClient* @throws IOException*/public static void release(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException {// 釋放資源if (httpResponse != null) {httpResponse.close();}if (httpClient != null) {httpClient.close();}}}
測試類:放在src/test/java包中
import java.util.HashMap;
import java.util.Map;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;import com.Application;
import com.cc.util.HttpClientResult;
import com.cc.util.HttpClientUtils;/*** Description: HttpClientUtils工具類測試* * @author JourWon* @date Created on 2018年4月19日*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class HttpClientUtilsTest {/*** Description: 測試get無參請求* * @throws Exception*/// @Testpublic void testGet() throws Exception {HttpClientResult result = HttpClientUtils.doGet("http://127.0.0.1:8080/hello/get");System.out.println(result);}/*** Description: 測試get帶參請求* * @throws Exception*/// @Testpublic void testGetWithParam() throws Exception {Map<String, String> params = new HashMap<String, String>();params.put("message", "helloworld");HttpClientResult result = HttpClientUtils.doGet("http://127.0.0.1:8080/hello/getWithParam", params);System.out.println(result);}/*** Description: 測試post帶請求頭不帶請求參數* * @throws Exception*/// @Testpublic void testPost() throws Exception {Map<String, String> headers = new HashMap<String, String>();headers.put("Cookie", "123");headers.put("Connection", "keep-alive");headers.put("Accept", "application/json");headers.put("Accept-Language", "zh-CN,zh;q=0.9");headers.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");HttpClientResult result = HttpClientUtils.doPost("http://127.0.0.1:8080/hello/post", headers, null);System.out.println(result);}/*** Description: 測試post帶參請求* * @throws Exception*/@Testpublic void testPostWithParam() throws Exception {Map<String, String> params = new HashMap<String, String>();params.put("code", "0");params.put("message", "helloworld");HttpClientResult result = HttpClientUtils.doPost("http://127.0.0.1:8080/hello/postWithParam", params);System.out.println(result);}}
三、總結
上面的代碼較為繁瑣,這里總結一下。
3.1、TCP通信如何實現
1、創建兩個線程類,分別用于發送消息和接收消息;
2、基于通信的輸出流實例化PringWriter對象,發送消息的線程每次通過PringWriter.println發送一條信息
3、基于通信的輸入流實例化BufferReader對象,接收消息的線程每次通過readLine讀取一行數據
4、Main線程中,服務端實例化ServerSocket對象、客戶端用實例化Socket對象,然后各自獲取輸入和輸出流并創建發送消息和接收消息的線程。(注意服務端要先執行accept方法,監聽端口)
3.2、UDP通信如何實現
1、創建一個緩存數據的字節數組,然后基于這個數組實例化DatagramPacket,然后創建接收消息的DatagramSocket,然后調用receive方法
2、將需要發送的數據轉換成字節數組,然后創建一個DatagramPacket對象,然后創建發送消息的DatagramSocket,然后調用send方法
3、注意發送和接收的差異:接收方在實例化DatagramSocket時指定ip和端口,發送發在實例化DatagramPacket時指定ip和端口。
多播這里就省略了,跟UDP差不多。
3.3、用JDK原生API
1、實例化URL對象
2、獲得一個URLConnection實例
3、獲得輸入流并包裝
3.4、HttpClient
1、首先創建一個默認的httpclient對象(CloseableHttpClient)
2、實例化URIBuilder對象(httpget才有)
3、基于URIBuilder實例化httpget對象
4、httpGet或者httpPost.setConfig
5、設置請求頭,設置請求參數(post方法才有)
6、執行請求httpClient.execute(httpMethod),并且返回一個CloseableHttpResponse對象
7、調用httpresponse對象的兩個方法getStatusLine、getEntity
8、關閉資源
?
HttpClient代碼參考https://www.jianshu.com/p/9504ecc7abad