【JAVA基礎篇】Socket編程

一、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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/530412.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/530412.shtml
英文地址,請注明出處:http://en.pswp.cn/news/530412.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【JAVA基礎篇】注解

一、什么是注解&#xff1f; 注解是元數據&#xff0c;所謂元數據就是描述數據的數據。 在annotation誕生之前&#xff08;jdk1.5誕生了注解&#xff09;&#xff0c;甚至之后&#xff0c;xml被廣泛的由于描述元數據。但是后來&#xff0c;有一些應用開發工程師和架構師覺得它…

【JAVA基礎篇】IO流

一、流的概念 “對語言設計人員來說&#xff0c;創建好的輸入&#xff0f;輸出系統是一項特別困難的任務。” ――《Think in Java》 無論是系統、還是語言的設計中IO的設計都是異常復雜的。面臨的最大的挑戰一般是如何覆蓋所有可能的因素&#xff0c;我們不僅僅要考慮文件、…

SpringMVC注解

一&#xff0c;RequestMapping 可以用在類和方法上 1.1 作用&#xff1a; 將客戶端請求映射到可匹配的類和方法中 1.2 屬性&#xff1a; name 給映射指定一個名字 path(同value相同&#xff09; 請求的url&#xff0c;path{"/mixedAttribute1","/mixedA…

【JAVA基礎篇】運算符

一、表達式 表達式由運算符和操作數組成 例如&#xff1a; 5 num1 num1num2 sumnum1num2 二、運算符分類 算數運算符、賦值運算符、關系運算符、邏輯運算符、條件運算符、位運算符 三、算數運算符 四、賦值運算符 格式&#xff1a;變量表達式 例如&#xff1a;int n3…

a4紙網頁打印 table_打印模板不愁人,你還在打印單調的A4紙嗎?

軟件介紹早在幾年前&#xff0c;社會上就已經開始了數字化、無紙化的推廣&#xff0c;但是就算再怎么無紙化&#xff0c;紙張還是有它必要的存在&#xff0c;在工作、學習過程中&#xff0c;打印的需求也必不可少的。但是一般的打印都是比較平庸的&#xff0c;要做會議記錄&…

IP地址、子網掩碼、網關、默認網關、DNS的理解

IP地址 Internet上為了區分數以億計的主機而給每個主機分配一個專門的地址&#xff0c;通過IP地址可以訪問到每臺主機。 子網掩碼 子網掩碼又稱網絡掩碼、地址掩碼、子網絡遮罩。它是用來指明一個IP地址哪些位標識的是主機所在的子網&#xff0c;以及哪些位標識的是主機的位…

上證指數30年k線圖_技術預判2020:上證指數要突破3500點才會“井噴”

2019年的行情很快就要收官了&#xff0c;截止目前&#xff0c;上證指數今年的漲幅是20.5%&#xff0c;不過可能有部分投資者今年的收益率還沒達到大盤指數的平均水平。不管怎樣&#xff0c;今年很快就要翻篇了&#xff0c;關鍵是看2020年股市能不能迎來更好的行情了。而總結得失…

【JAVA基礎篇】基本數據類型及自動類型轉換

一、8種基本數據類型以及占用內存空間大小 boolean 1byte或4byte byte 8bit/1byte char 16bit/2byte short 16bit/2byte float 32bit/4byte int 32bit/4byte long 64bit/8byte double 64bit/8byte 二、自動類型轉換 …

的優缺點_淺談桉木家具的優缺點

家具現在的材質是有很多的&#xff0c;木質的&#xff0c;石材的&#xff0c;還有真空充氣的&#xff0c;都是很不錯的類型。桉木家具是現在很多人都喜歡的一種材質&#xff0c;但是很多人對桉木家具的優缺點不是很清楚&#xff0c;為了能夠讓大家更加清楚的了解桉木家具&#…

【算法篇】遞歸

一、遞歸的概念 程序調用自身的編程技巧稱為遞歸。 遞歸的核心思想就是將一個大規模復雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解。 二、遞歸的優點 使用遞歸的好處是只需要少量的代碼就可以描述出求解問題過程中多次重復的計算&#xff0c;大大減少了程序…

客戶說發貨慢怎么回復_女生微信說身體不舒服怎么回復關心她?

當你不在女生身邊&#xff0c;女生微信給你說身體不舒服&#xff0c;肯定需要說點話來安慰她了。多喝熱水肯定是不行了&#xff0c;一點用處都沒有&#xff0c;還會讓女生覺得你根本不重視她&#xff0c;是在敷衍她&#xff0c;那女生微信說身體不舒服怎么回復關心她呢&#xf…

【算法篇】八種內排序算法

常用的八種內排序算法分別是&#xff1a; 交換排序&#xff1a;冒泡排序、快速排序選擇排序&#xff1a;簡單選擇排序、堆排序插入排序&#xff1a;直接插入排序、希爾排序歸并排序基數排序 內排序巧記&#xff1a;選(選擇)艦(簡單選擇)隊(堆)的時候腳(交換)毛(冒泡)快(快速)&…

數據分析專題報告范文6篇_小學生看圖寫話范文:小熊玩蹺蹺板?(6篇),讓孩子參考練習...

?范文01&#xff1a;小熊蹺蹺板一天&#xff0c;天氣晴朗&#xff0c;胖乎乎的小熊和小白兔一起玩蹺蹺板。小熊一屁股坐在地上&#xff0c;小白兔說&#xff1a;“啊&#xff01;我有恐高癥哇&#xff01;”小熊說&#xff1a;“我比你重&#xff0c;所以你沒有辦法把我翹起來…

PL/SQL

1 PL/SQLPL/SQL:過程化SQL語言&#xff08;Procedural Language/SQL&#xff09;。PL/SQL是Oracle數據庫對SQL語句的擴展。在普通SQL語句的使用上增加了編程語言的特點&#xff0c;所以PL/SQL把數據操作和查詢語句組織在PL/SQL代碼的過程性單元中&#xff0c;通過邏輯判斷、循環…

20sccm_SCCM 2012安裝圖解教程(一步一步詳細步驟)

本系列文章的環境架構如下圖所示&#xff1a;所有服務器安裝的操作系統都是windows Server 2008 R2 中文企業版。計算機名軟件、版本及角色SC-DC.SC.COMwindows Server 2008 R2 Enterprise /Active Directory 2008 R2SC-SQL.SC.COMSQL Server 2008 R2 EnterpriseSC-SCCM.SC.COM…

【Java中級篇】Dom4j解析xml數據

一、依賴 <dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency> 二、test.xml <?xml version"1.0" encoding"UTF-8"?> <students>…

redis 附近的人_使用redis—geo api實現搜索附近的人,自己寫的一個composer包

安裝如果是應用在項目當中的話找到根目錄&#xff0c;需要和 composer.json同級composer require gaopengfei/redis_lbs基本操作初始化require_once __DIR__./vendor/autoload.php;$lbs new \LBS\Services\LBSService();添加$add_params [[name > yabao_road,long > 11…

【Java中級篇】使用zxing生成二維碼

一、pom.xml添加依賴 <dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.1.0</version></dependency><dependency><groupId>com.google.zxing</groupId><artifa…

微信小程序彈出框豎向滾動_微信小程序 解決自定義彈出層滑動時下層頁面滾動問題...

WXML將整個底層頁面使用 scroll-view 包裹起來&#xff0c;設置 scroll-y 當顯示彈出層的時候為 true&#xff0c; 閉關彈出層的時候為 falseWXSSPage 設置為絕對定位&#xff0c;寬高各百分之百 &#xff0c; scroll-view 高度 百分之百Page{position: absolute;width: 100%;h…

win10環境安裝使用svn客戶端和服務端

一、下載安裝包 安裝包下載傳送門http://subversion.apache.org/packages.html 無法下載的童鞋去百度云下載 鏈接&#xff1a;https://pan.baidu.com/s/1EuGohoZKIPmRvynp5-Subw 提取碼&#xff1a;ohna 鏈接&#xff1a;https://pan.baidu.com/s/1EJrd5DzGCBE4rRfdhuno6Q …