【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.mzph.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 …