Java之旅–通讯
最后更新于:2022-04-01 20:12:09
# 概述
通讯,源于网络,网络从下到上,分为7层:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层;通讯,基于协议,常用的协议有很多,比如网络层协议IP、传输层协议TCP/UDP、应用层协议HTTP/SOAP/REST等。
我们还会经常听到Socket,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。有个形象的比喻:HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
任何语言,都会提供对通讯的支持,我们今天用Java语言,演示几种在Java中比较常用的通讯技术,包括:Socket、Http、REST、Dubbo、Thrift、FTP。
# Socket
**SocketServer.java**
~~~
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) {
SocketServer ss = new SocketServer();
//设定服务端的端口号
ServerSocket s = null;
try {
s = new ServerSocket(10099);
System.out.println("ServerSocket Start:"+s);
while(true)
{
Socket socket = s.accept(); //这个地方是阻塞的
System.out.println("Server_Accept:"+socket);
SocketServer.HandleSocket vsm = ss.new HandleSocket(socket);
new Thread(vsm).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally
{
try {
s.close();
} catch (IOException e) {
}
}
}
class HandleSocket implements Runnable
{
private Socket socket;
public HandleSocket(Socket socket)
{
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
PrintWriter pw = null;
try {
//用于接收客户端发来的请求
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//用于发送返回信息,可以不需要装饰这么多io流使用缓冲流时发送数据要注意调用.flush()方法
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
String str = br.readLine();
if(str != null)
{
pw.println("OK_"+Thread.currentThread().getName());
pw.flush();
System.out.println("Command:"+str);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println("Server_Close:"+socket);
try {
br.close();
pw.close();
socket.close();
} catch (Exception e2) {
}
}
}
}
}
~~~
**SocketClient.java**
~~~
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class SocketClient {
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<10;i++) {
call( "Hello" + i);
}
}
public static void call(String command) {
Socket socket = null;
BufferedReader br = null;
PrintWriter pw = null;
try {
//客户端socket指定服务器的地址和端口号
socket = new Socket("127.0.0.1", 10099);
System.out.println("Client_Open" + socket + " Command:" + command);
//同服务器原理一样
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
pw.println(command);
pw.flush();
String str = br.readLine();
System.out.println("Client_Receive:"+str);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
System.out.println("Client_Close:"+socket);
br.close();
pw.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
~~~
# Http
### **HttpConnection演示**
~~~
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpConnectionTest {
public static void main(String[] args) throws Exception {
System.out.println("begin send");
String inputParam = "Test ";
URL url = null;
HttpURLConnection httpConn = null;
OutputStream output = null;
OutputStreamWriter outr = null;
//url = new URL("http://127.0.0.1:8888/iotest/ReadServlet");
url = new URL("http://www.baidu.com");
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setConnectTimeout(30000);
httpConn.setReadTimeout(30000);
HttpURLConnection.setFollowRedirects(true);
httpConn.setDoOutput(true);
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Content-Type", "text/xml");
httpConn.connect();
output = httpConn.getOutputStream();
outr = new OutputStreamWriter(output);
// 写入请求参数
outr.write(inputParam.toString().toCharArray(), 0, inputParam.toString().length());
outr.flush();
outr.close();
System.out.println("send ok");
int code = httpConn.getResponseCode();
System.out.println("code " + code);
System.out.println(httpConn.getResponseMessage());
//读取响应内容
String sCurrentLine = "";
String sTotalString = "";
if (code == 200)
{
java.io.InputStream is = httpConn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
while ((sCurrentLine = reader.readLine()) != null)
if (sCurrentLine.length() > 0)
sTotalString = sTotalString + sCurrentLine.trim();
} else
{
sTotalString = "远程服务器连接失败,错误代码:" + code;
}
System.out.println("response:" + sTotalString);
}
}
~~~
### **Apache HttpComponents**
官网:[http://hc.apache.org/](http://hc.apache.org/)
已经废弃的Jar(这个项目,自从2007年8月份就不再维护了,已经迁移到org.apache.httpcomponents了):
~~~
commons-httpclient
commons-httpclient
3.1
~~~
org.apache.httpcomponents maven依赖:
~~~
org.apache.httpcomponents
httpcore
4.2.3
org.apache.httpcomponents
httpclient
4.2.3
~~~
Apache HttpComponents代码演示:
~~~
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import com.creditease.ns.oltp.tunneladapter.tunnels.cb.util.CbConst;
/**
* 更多参考网址:http://hc.apache.org/
*
*/
public class HttpClientTest {
static String url = "http://www.tuicool.com/";
public static void main(String[] args) throws HttpException, IOException {
testHttp2();
}
public static void testHttp2() {
org.apache.http.client.HttpClient httpClient = null;
HttpPost httpPost = null;
InputStream in = null;
httpClient = new DefaultHttpClient();
httpPost = new HttpPost(url);
String resp = "";
try {
httpClient = new DefaultHttpClient();
httpPost = new HttpPost(url);
if (url.indexOf("https") != -1) {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
in = new FileInputStream("quick.keystore");
keyStore.load(in, "chinabank".toCharArray());
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore);
Scheme scheme = new Scheme("https", 443, sslSocketFactory);
httpClient.getConnectionManager().getSchemeRegistry().register(scheme);
}
HttpParams httpParams = httpClient.getParams();
httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000 * 20);
httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000 * 40);
List reqPair = new ArrayList();
reqPair.add(new BasicNameValuePair("charset", "UTF-8"));
reqPair.add(new BasicNameValuePair("req", "nihao"));
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(reqPair, "UTF-8");
httpPost.setEntity(urlEncodedFormEntity);
HttpResponse response = httpClient.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
InputStream is = responseEntity.getContent();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int ch = 0;
while ((ch = is.read(buffer)) != -1) {
baos.write(buffer, 0, ch);
}
byte bytes[] = baos.toByteArray();
resp = new String(bytes, CbConst.ENCODING);
}
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("statusCode:" + statusCode);
System.out.println("resp:" + resp);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
httpPost.releaseConnection();
httpClient.getConnectionManager().shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
~~~
quick.keystore,是一个密码保护的文件,存放私钥和证书。可以通过JDK自带的keytool工具生成。只要有密码,可以提取出私钥和证书。参考网址:
[从Java Keystore文件中提取私钥、证书](http://blog.csdn.net/moreorless/article/details/4985940)[](#)
[keystore提取私钥和证书](http://blog.csdn.net/madun/article/details/8677783)
[java 调用 keytool 生成keystore 和 cer 证书](http://blog.csdn.net/saindy5828/article/details/11987587)
[KeyStore中的别名](http://blog.csdn.net/peterwanghao/article/details/1771723)
### 一个Http压测例程
~~~
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HttpSocketPressTest {
private Long succNum = 0L;
private Long failNum = 0L;
private Long connFailNum = 0L;
private Long readFailNum = 0L;
private Long otherFailNum = 0L;
private Long beginTime = System.currentTimeMillis();
private Boolean recordSuccReturn = false;
private String url = "http://www.baidu.com";
public static final int ThreadNum = 100;
public static final int MonitorInterval = 3;
public static void main(String[] args) throws Exception {
createDir("d:\\httptest\\");
createDir("d:\\httptest\\succ\\");
createDir("d:\\httptest\\fail\\");
createDir("d:\\httptest\\exec\\");
createDir("d:\\httptest\\execother\\");
HttpSocketPressTest hspt = new HttpSocketPressTest();
if ((args != null) && (args.length > 0)) {
hspt.setRecordSuccReturn(true);
}
System.out.println("开" + ThreadNum + "个线程,测试用使用Socket5[10.100.140.85:1080]代理Get方式访问外网。");
System.out.println("如果需要对200返回也记录返回内容,请运行程序时输入任意参数,记录位置d:/httptest/succ。");
System.out.println("异常及非200返回会写在d:/httptest/exception和fail目录。");
ExecutorService executorService = Executors.newFixedThreadPool(ThreadNum);
for (int i = 0; i < ThreadNum; i++) {
executorService.submit(new HttpConn(hspt));
}
Monitor m = new Monitor(hspt);
Timer timer = new Timer();
timer.schedule(m, MonitorInterval * 1000, MonitorInterval * 1000);
}
public synchronized void IncreaseSuccNum() {
succNum++;
}
public synchronized void IncreaseFailNum() {
failNum++;
}
public synchronized void IncreaseConnFailNum() {
connFailNum++;
}
public synchronized void IncreaseReadFailNum() {
readFailNum++;
}
public synchronized void IncreaseOtherFailNum() {
otherFailNum++;
}
public synchronized Long getSuccNum() {
return succNum;
}
public synchronized Long getFailNum() {
return failNum;
}
public synchronized Long getConnFailNum() {
return connFailNum;
}
public synchronized Long getReadFailNum() {
return readFailNum;
}
public synchronized Long getOtherFailNum() {
return otherFailNum;
}
public Long getBeginTime() {
return beginTime;
}
public void setRecordSuccReturn(Boolean b) {
recordSuccReturn = b;
}
public Boolean getRecordSuccReturn() {
return recordSuccReturn;
}
public String getUrl() {
return url;
}
private static void createDir(String dir) {
File f = new File(dir);
if (!f.exists()) {
f.mkdir();
}
}
}
class Monitor extends TimerTask {
private HttpSocketPressTest hspt;
private Long times = 0L;
public Monitor(HttpSocketPressTest hspt) {
this.hspt = hspt;
}
@Override
public void run() {
Long nowTime = System.currentTimeMillis();
Long interval = nowTime - hspt.getBeginTime();
System.out.println("");
System.out.println("间隔" + HttpSocketPressTest.MonitorInterval + "秒报告一次结果,这是第" + ++times + "次,如下:");
System.out.println("当前时间:" + new Date());
System.out.println("累计运行时间(单位秒):" + (interval / 1000));
System.out.println("成功次数:" + hspt.getSuccNum() + ";平均每秒成功次数:" + ((hspt.getSuccNum() * 1000) / interval));
System.out.println("失败次数:" + hspt.getFailNum() + "【Connection timed out:" + hspt.getConnFailNum()
+ ",Read timed out:" + hspt.getReadFailNum() + ",其他异常:" + hspt.getOtherFailNum() + "】");
}
}
class HttpConn implements Runnable {
private HttpSocketPressTest hspt;
public HttpConn(HttpSocketPressTest hspt) {
this.hspt = hspt;
}
@Override
public void run() {
while (true) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
String d = sdf.format(new Date());
// System.out.println("[" + d + "] [" + Thread.currentThread().getName() +
// "] [访问外网]");
// long startTime = System.currentTimeMillis();
String strUrl = hspt.getUrl();
String domainName = strUrl.split("\\.")[1];
HttpURLConnection httpConn = null;
BufferedReader reader = null;
try {
URL url = null;
url = new URL(strUrl);
Proxy p;
p = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("10.100.140.85", 1080));
httpConn = (HttpURLConnection) url.openConnection(p);
httpConn.setConnectTimeout(30 * 1000);
httpConn.setReadTimeout(30 * 1000);
HttpURLConnection.setFollowRedirects(true);
httpConn.setDoOutput(true);
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Content-Type", "text/xml");
for (Entry> m : httpConn.getHeaderFields().entrySet()) {
// System.out.println(m.getKey() + ":" + m.getValue());
}
// 这行代码会报:java.net.ConnectException: Connection timed out: connect
httpConn.connect();
int code = httpConn.getResponseCode();
// 读取响应内容
String sCurrentLine = "";
StringBuilder sb = new StringBuilder();
if (code == 200) {
hspt.IncreaseSuccNum();
} else {
hspt.IncreaseFailNum();
}
sb.append("StatusCode:").append(code).append("\n");
sb.append("url:").append(strUrl).append("\n");
InputStream is = httpConn.getInputStream();
reader = new BufferedReader(new InputStreamReader(is));
// 这行代码会报:java.net.SocketTimeoutException: Read timed out
while ((sCurrentLine = reader.readLine()) != null) {
if (sCurrentLine.length() > 0) {
sb.append(sCurrentLine.trim());
}
}
// System.out.println("接收内容大小:" + sb.toString().length());
if (hspt.getRecordSuccReturn() && (code == 200)) {
writeFile("d:\\httptest\\succ\\" + d + "_" + UUID.randomUUID() + ".html", sb.toString());
}
if (code != 200) {
writeFile("d:\\httptest\\fail\\" + d + "_" + UUID.randomUUID() + ".html", sb.toString());
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(new Date());
hspt.IncreaseFailNum();
String msg = e.toString();
Boolean isOther = false;
if (msg.indexOf("Connection timed out") != -1) {
hspt.IncreaseConnFailNum();
} else if (msg.indexOf("Read timed out") != -1) {
hspt.IncreaseReadFailNum();
} else {
isOther = true;
hspt.IncreaseOtherFailNum();
}
if (isOther) {
writeFile("d:\\httptest\\execother\\" + domainName + "_" + d + "_" + UUID.randomUUID() + ".txt", strUrl
+ "\r\n" + e);
} else {
writeFile("d:\\httptest\\exec\\" + domainName + "_" + d + "_" + UUID.randomUUID() + ".txt", strUrl + "\r\n"
+ e);
}
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (httpConn != null) {
httpConn.disconnect();
}
// System.out.println("[" + d + "] [" + Thread.currentThread().getName()
// + "] [访问外网] [cost:["
// + (System.currentTimeMillis() - startTime) + "ms]]");
}
}
}
private void writeFile(String name, String content) {
FileWriter writer = null;
try {
// 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
writer = new FileWriter(name, false);
writer.write(content);
writer.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
~~~
# Dubbo
...待续
# Thrift
...待续
# Rest
...待续
# FTP
...待续
';