牛骨文教育服务平台(让学习变的简单)

Java之旅--通讯

概述

通讯,源于网络,网络从下到上,分为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 = "<?xml version="1.0" encoding="UTF-8"?><content>Test</content></page>";
		
		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/

已经废弃的Jar(这个项目,自从2007年8月份就不再维护了,已经迁移到org.apache.httpcomponents了):

<dependency>
	<groupId>commons-httpclient</groupId>
	<artifactId>commons-httpclient</artifactId>
	<version>3.1</version>
</dependency>

org.apache.httpcomponents maven依赖:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.2.3</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2.3</version>
</dependency>

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<NameValuePair> reqPair = new ArrayList<NameValuePair>();
			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文件中提取私钥、证书

keystore提取私钥和证书

java 调用 keytool 生成keystore 和 cer 证书

KeyStore中的别名

一个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:httptestsucc");
		createDir("d:httptestfail");
		createDir("d:httptestexec");
		createDir("d:httptestexecother");

		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<String, List<String>> 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("
");
				sb.append("url:").append(strUrl).append("
");
				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:httptestsucc" + d + "_" + UUID.randomUUID() + ".html", sb.toString());
				}
				if (code != 200) {
					writeFile("d:httptestfail" + 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:httptestexecother" + domainName + "_" + d + "_" + UUID.randomUUID() + ".txt", strUrl
							+ "
" + e);
				} else {
					writeFile("d:httptestexec" + domainName + "_" + d + "_" + UUID.randomUUID() + ".txt", strUrl + "
"
							+ 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

...待续