【JAVA基础篇】Socket编程
一、Socket的概念
Socket是一種通訊機(jī)制,通常稱為套接字。英文原意是插座,顧明思義,Socket像是一個多孔插座,可以提供多個端口的連接服務(wù)
ps:至于socket在計算機(jī)術(shù)語中怎么就翻譯成了“套接字”這個令人費解的詞,這真是未解之謎。
二、Java Socket編程示例
2.1、基于TCP協(xié)議
tcp協(xié)議是面向連接的,通常會有服務(wù)端和客戶端,服務(wù)端和客戶端先連接,然后傳遞消息。
SendMsg:用于創(chuàng)建發(fā)送消息的線程
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter;/*** 發(fā)送消息* @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:用于創(chuàng)建接收消息的線程
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() + "讀到一行數(shù)據(jù):" + msg);} catch (IOException e) {e.printStackTrace();System.exit(0);}}} }Server:服務(wù)端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /*** 服務(wù)端* @author cc**/ public class Server {public static void main(String[] args) {ServerSocket socket;try {socket = new ServerSocket(30000);System.out.println("我已經(jīng)開啟服務(wù)了!");Socket client = socket.accept();System.out.println("有客戶端連接進(jìn)來!");InputStream is = client.getInputStream();OutputStream os = client.getOutputStream();Thread thread = new Thread(new RecevieMsg(is), "服務(wù)端接收線程");Thread thread2 = new Thread(new SendMsg(os), "服務(wù)端發(fā)送線程");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), "客戶端發(fā)送線程");thread.start();thread2.start();} catch (IOException e) {e.printStackTrace();}} }啟動Server后再啟動Client,然后就可以通過兩個控制臺聊天啦!
擴(kuò)展:用上面的代碼,如果是一個服務(wù)端和一個客戶端的話通過控制臺進(jìn)行收發(fā)消息沒有問題,但是一個服務(wù)端和多個客戶端(Client類執(zhí)行多次)的話會有問題,理由是:當(dāng)你用服務(wù)端的終端發(fā)送消息的時候,這個消息應(yīng)該發(fā)給哪個客戶端呢?實際情況是當(dāng)服務(wù)端終端發(fā)送消息的條數(shù)達(dá)到客戶端的數(shù)量時,數(shù)據(jù)才會發(fā)出去,并且第一條對應(yīng)的發(fā)給第一個客戶端。
因此我們不同通過服務(wù)端終端來給客戶端發(fā)送消息,更改了Server類并且新增了ServerHandleClientMsg類
Server
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /*** 服務(wù)端* @author cc**/ public class Server {public static void main(String[] args) {try {ServerSocket socket = new ServerSocket(30000);System.out.println("我已經(jīng)開啟服務(wù)了!");while(true){Socket client = socket.accept();System.out.println("有客戶端連接進(jìn)來!");InputStream is = client.getInputStream();OutputStream os = client.getOutputStream();Thread thread = new Thread(new ServerHandleClientMsg(is,os), "服務(wù)端處理客戶端信息");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;/*** 服務(wù)端處理客戶端信息* @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()+"的線程讀到一行數(shù)據(jù):"+msg);PrintWriter pw = new PrintWriter(os);pw.println("服務(wù)端已收到您的消息:"+msg);pw.flush();} catch (IOException e) {e.printStackTrace();endFlag = true;}if(endFlag){break;}}}}2.2、基于UDP協(xié)議
udp協(xié)議是無連接的,并且是不可靠的,直接向網(wǎng)絡(luò)發(fā)送數(shù)據(jù)報。
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地址為本地機(jī)器地址InetAddress ip = InetAddress.getLocalHost();int port = 8888;// 創(chuàng)建接收方的套接字,并指定端口號和IP地址DatagramSocket getSocket = new DatagramSocket(port, ip);// 確定數(shù)據(jù)報接受的數(shù)據(jù)的數(shù)組大小byte[] buf = new byte[1024];// 創(chuàng)建接受類型的數(shù)據(jù)報,數(shù)據(jù)將存儲在buf中DatagramPacket getPacket = new DatagramPacket(buf, buf.length);// 通過套接字接收數(shù)據(jù)getSocket.receive(getPacket);// 解析發(fā)送方傳遞的消息,并打印String getMes = new String(buf, 0, getPacket.getLength());System.out.println("對方發(fā)送的消息:" + getMes);// 通過數(shù)據(jù)報得到發(fā)送方的IP和端口號,并打印InetAddress sendIP = getPacket.getAddress();int sendPort = getPacket.getPort();System.out.println("對方的IP地址是:" + sendIP.getHostAddress());System.out.println("對方的端口號是:" + sendPort);// 通過數(shù)據(jù)報得到發(fā)送方的套接字地址SocketAddress sendAddress = getPacket.getSocketAddress();// 確定要反饋發(fā)送方的消息內(nèi)容,并轉(zhuǎn)換為字節(jié)數(shù)組String feedback = "接收方說:我收到了!";byte[] backBuf = feedback.getBytes();// 創(chuàng)建發(fā)送類型的數(shù)據(jù)報DatagramPacket sendPacket = new DatagramPacket(backBuf,backBuf.length, sendAddress);// 通過套接字發(fā)送數(shù)據(jù)getSocket.send(sendPacket);// 關(guān)閉套接字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 {// 創(chuàng)建發(fā)送方的套接字,IP默認(rèn)為本地,端口號隨機(jī)DatagramSocket sendSocket = new DatagramSocket();// 確定要發(fā)送的消息:String mes = "你好!接收方!";// 數(shù)據(jù)報的數(shù)據(jù)是以字節(jié)數(shù)組的形式存儲的byte[] buf = mes.getBytes();// 確定發(fā)送方的IP地址及端口號,地址為本地機(jī)器地址int port = 8888;InetAddress ip = InetAddress.getLocalHost();// 創(chuàng)建發(fā)送類型的數(shù)據(jù)報:DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, ip, port);// 通過套接字發(fā)送數(shù)據(jù):sendSocket.send(sendPacket);// 確定接受反饋數(shù)據(jù)的緩沖存儲器,即存儲數(shù)據(jù)的字節(jié)數(shù)組byte[] getBuf = new byte[1024];// 創(chuàng)建接受類型的數(shù)據(jù)報DatagramPacket getPacket = new DatagramPacket(getBuf, getBuf.length);// 通過套接字接受數(shù)據(jù)sendSocket.receive(getPacket);// 解析反饋的消息,并打印String backMes = new String(getBuf, 0, getPacket.getLength());System.out.println("接受方返回的消息:" + backMes);// 關(guān)閉套接字sendSocket.close();} catch (Exception e) {e.printStackTrace();}} }先啟動Receive,在啟動Sender
上面的例子是點對點通信,下面我們來看如何進(jìn)行多播
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()是阻塞方法,會等待客戶端發(fā)送過來的信息 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協(xié)議
其實HTTP協(xié)議是TCP的一種。下面介紹了Java中基于HTTP協(xié)議的兩種通信方式。
2.3.1 URLConnection
JDK提供的基于HTTP協(xié)議的api實現(xiàn)
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協(xié)議的通信方式,相比JDK自帶的URLConnection,增加了易用性和靈活性。
下面給出了HttpClient的簡單Demo。環(huán)境:JDK1.8,用Maven構(gòu)建,使用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>應(yīng)用啟動類
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帶參請求成功,參數(shù)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帶參請求成功,參數(shù)code: " + code + ",參數(shù)message: " + message;}}HttpClient響應(yīng)結(jié)果
package com.cc.util; import java.io.Serializable;/*** Description: 封裝httpClient響應(yīng)結(jié)果* * @author JourWon* @date Created on 2018年4月19日*/ public class HttpClientResult implements Serializable {/*** 響應(yīng)狀態(tài)碼*/private int code;/*** 響應(yīng)數(shù)據(jù)*/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發(fā)送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 {// 編碼格式。發(fā)送編碼格式統(tǒng)一用UTF-8private static final String ENCODING = "UTF-8";// 設(shè)置連接超時時間,單位毫秒。private static final int CONNECT_TIMEOUT = 6000;// 請求獲取數(shù)據(jù)的超時時間(即響應(yīng)時間),單位毫秒。private static final int SOCKET_TIMEOUT = 6000;/*** 發(fā)送get請求;不帶請求頭和請求參數(shù)* * @param url* 請求地址* @return* @throws Exception*/public static HttpClientResult doGet(String url) throws Exception {return doGet(url, null, null);}/*** 發(fā)送get請求;帶請求參數(shù)* * @param url* 請求地址* @param params* 請求參數(shù)集合* @return* @throws Exception*/public static HttpClientResult doGet(String url, Map<String, String> params) throws Exception {return doGet(url, null, params);}/*** 發(fā)送get請求;帶請求頭和請求參數(shù)* * @param url* 請求地址* @param headers* 請求頭集合* @param params* 請求參數(shù)集合* @return* @throws Exception*/public static HttpClientResult doGet(String url, Map<String, String> headers, Map<String, String> params)throws Exception {// 創(chuàng)建httpClient對象CloseableHttpClient httpClient = HttpClients.createDefault();// 創(chuàng)建訪問的地址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());}}// 創(chuàng)建http對象HttpGet httpGet = new HttpGet(uriBuilder.build());/*** setConnectTimeout:設(shè)置連接超時時間,單位毫秒。* setConnectionRequestTimeout:設(shè)置從connect Manager(連接池)獲取Connection* 超時時間,單位毫秒。這個屬性是新加的屬性,因為目前版本是可以共享連接池的。* setSocketTimeout:請求獲取數(shù)據(jù)的超時時間(即響應(yīng)時間),單位毫秒。* 如果訪問一個接口,多少時間內(nèi)無法返回數(shù)據(jù),就直接放棄此次調(diào)用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpGet.setConfig(requestConfig);// 設(shè)置請求頭packageHeader(headers, httpGet);// 創(chuàng)建httpResponse對象CloseableHttpResponse httpResponse = null;try {// 執(zhí)行請求并獲得響應(yīng)結(jié)果return getHttpClientResult(httpResponse, httpClient, httpGet);} finally {// 釋放資源release(httpResponse, httpClient);}}/*** 發(fā)送post請求;不帶請求頭和請求參數(shù)* * @param url* 請求地址* @return* @throws Exception*/public static HttpClientResult doPost(String url) throws Exception {return doPost(url, null, null);}/*** 發(fā)送post請求;帶請求參數(shù)* * @param url* 請求地址* @param params* 參數(shù)集合* @return* @throws Exception*/public static HttpClientResult doPost(String url, Map<String, String> params) throws Exception {return doPost(url, null, params);}/*** 發(fā)送post請求;帶請求頭和請求參數(shù)* * @param url* 請求地址* @param headers* 請求頭集合* @param params* 請求參數(shù)集合* @return* @throws Exception*/public static HttpClientResult doPost(String url, Map<String, String> headers, Map<String, String> params)throws Exception {// 創(chuàng)建httpClient對象CloseableHttpClient httpClient = HttpClients.createDefault();// 創(chuàng)建http對象HttpPost httpPost = new HttpPost(url);/*** setConnectTimeout:設(shè)置連接超時時間,單位毫秒。* setConnectionRequestTimeout:設(shè)置從connect Manager(連接池)獲取Connection* 超時時間,單位毫秒。這個屬性是新加的屬性,因為目前版本是可以共享連接池的。* setSocketTimeout:請求獲取數(shù)據(jù)的超時時間(即響應(yīng)時間),單位毫秒。* 如果訪問一個接口,多少時間內(nèi)無法返回數(shù)據(jù),就直接放棄此次調(diào)用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpPost.setConfig(requestConfig);// 設(shè)置請求頭/** 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);// 封裝請求參數(shù)packageParam(params, httpPost);// 創(chuàng)建httpResponse對象CloseableHttpResponse httpResponse = null;try {// 執(zhí)行請求并獲得響應(yīng)結(jié)果return getHttpClientResult(httpResponse, httpClient, httpPost);} finally {// 釋放資源release(httpResponse, httpClient);}}/*** 發(fā)送put請求;不帶請求參數(shù)* * @param url* 請求地址* @param params* 參數(shù)集合* @return* @throws Exception*/public static HttpClientResult doPut(String url) throws Exception {return doPut(url);}/*** 發(fā)送put請求;帶請求參數(shù)* * @param url* 請求地址* @param params* 參數(shù)集合* @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);}}/*** 發(fā)送delete請求;不帶請求參數(shù)* * @param url* 請求地址* @param params* 參數(shù)集合* @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);}}/*** 發(fā)送delete請求;帶請求參數(shù)* * @param url* 請求地址* @param params* 參數(shù)集合* @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) {// 設(shè)置到請求頭到HttpRequestBase對象中httpMethod.setHeader(entry.getKey(), entry.getValue());}}}/*** Description: 封裝請求參數(shù)* * @param params* @param httpMethod* @throws UnsupportedEncodingException*/public static void packageParam(Map<String, String> params, HttpEntityEnclosingRequestBase httpMethod)throws UnsupportedEncodingException {// 封裝請求參數(shù)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()));}// 設(shè)置到請求的http對象中httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));}}/*** Description: 獲得響應(yīng)結(jié)果* * @param httpResponse* @param httpClient* @param httpMethod* @return* @throws Exception*/public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception {// 執(zhí)行請求httpResponse = httpClient.execute(httpMethod);// 獲取返回結(jié)果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帶請求頭不帶請求參數(shù)* * @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);}}三、總結(jié)
上面的代碼較為繁瑣,這里總結(jié)一下。
3.1、TCP通信如何實現(xiàn)
1、創(chuàng)建兩個線程類,分別用于發(fā)送消息和接收消息;
2、基于通信的輸出流實例化PringWriter對象,發(fā)送消息的線程每次通過PringWriter.println發(fā)送一條信息
3、基于通信的輸入流實例化BufferReader對象,接收消息的線程每次通過readLine讀取一行數(shù)據(jù)
4、Main線程中,服務(wù)端實例化ServerSocket對象、客戶端用實例化Socket對象,然后各自獲取輸入和輸出流并創(chuàng)建發(fā)送消息和接收消息的線程。(注意服務(wù)端要先執(zhí)行accept方法,監(jiān)聽端口)
3.2、UDP通信如何實現(xiàn)
1、創(chuàng)建一個緩存數(shù)據(jù)的字節(jié)數(shù)組,然后基于這個數(shù)組實例化DatagramPacket,然后創(chuàng)建接收消息的DatagramSocket,然后調(diào)用receive方法
2、將需要發(fā)送的數(shù)據(jù)轉(zhuǎn)換成字節(jié)數(shù)組,然后創(chuàng)建一個DatagramPacket對象,然后創(chuàng)建發(fā)送消息的DatagramSocket,然后調(diào)用send方法
3、注意發(fā)送和接收的差異:接收方在實例化DatagramSocket時指定ip和端口,發(fā)送發(fā)在實例化DatagramPacket時指定ip和端口。
多播這里就省略了,跟UDP差不多。
3.3、用JDK原生API
1、實例化URL對象
2、獲得一個URLConnection實例
3、獲得輸入流并包裝
3.4、HttpClient
1、首先創(chuàng)建一個默認(rèn)的httpclient對象(CloseableHttpClient)
2、實例化URIBuilder對象(httpget才有)
3、基于URIBuilder實例化httpget對象
4、httpGet或者h(yuǎn)ttpPost.setConfig
5、設(shè)置請求頭,設(shè)置請求參數(shù)(post方法才有)
6、執(zhí)行請求httpClient.execute(httpMethod),并且返回一個CloseableHttpResponse對象
7、調(diào)用httpresponse對象的兩個方法getStatusLine、getEntity
8、關(guān)閉資源
?
HttpClient代碼參考https://www.jianshu.com/p/9504ecc7abad
總結(jié)
以上是生活随笔為你收集整理的【JAVA基础篇】Socket编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当中国武警需要什么学历,什么条件当武警需
- 下一篇: 【JAVA基础篇】IO流