这两天研究OkHttp3在Android端的使用,然后想探究其底层通信是怎么实现的。因为原来有个概念就是Android这边的第三方网络框架都是基于HttpClient或者HttpURLConnection来实现的,特别是现在基本上都是使用HttpURLConnection来实现网络通信,所以现在研究OkHttp3的时候,我一直以为其底层也是基于HttpURLConnection来实现网络通信的,及我以为OkHttp3是对HttpURLConnection的封装。但是在研究的过程中发现不是这样的,OkHttp3实际上是基于Socket来进行网络通信的,由于之前对Socket通信一无所知,所以为了进一步的研究OkHttp3,这里来对Socket的使用做一个基本的总结。
Socket是对TCP协议的一种实现,客户端和服务器端各有一个Socket对象,两个Socket对象建立连接后,就可以通过简单的读写操作来实现网络通信,本文就来实现一个Socket客户端和服务器端吧。
1、服务器端代码实现
try {
// 1、创建一个服务器端Socket,即ServerSocket,指定绑定端口
ServerSocket serverSocket = new ServerSocket(7779);
// 2、调用accept方法开始监听,等待客户端连接
Socket socket = serverSocket.accept();
// 3、获取输入流,并读取客户端信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String info = br.readLine();
while (info != null) {
System.out.println("i am server. message from client is " + info);
info = br.readLine();
}
socket.shutdownInput();//关闭输入流
// 4、获取输出流,响应客户端请求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("i want say welcome to you");
pw.flush();//不必等待,立即发送消息
// 5、关闭资源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
代码中的注释说的很详细,要注意的是accept方法会阻塞线程,直到同客户端建立连接并接收到客户端数据。之后通过socket.getInputStream获取客户端传递过来的消息,并使用BufferedReader对象来读取对象消息。
2、客户端代码实现
try {
// 1、创建客户端Socket,指定服务器地址以及端口
Socket socket = new Socket("localhost", 7779);
// 2、获取输出流,向服务器发送数据
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("this is my username and password");
pw.flush(); //不必等待,立即发送
socket.shutdownOutput();
// 3、获取输入流并读取服务器的响应数据
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = br.readLine();
while (info != null) {
System.out.println("i am client. server says that " + info);
info = br.readLine();
}
// 4、关闭资源
br.close();
is.close();
pw.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
客户端和服务器端读写数据方法一直,不过这里是客户端先写后读,而服务器是先读后写。测试时,我碰了一个坑,就是我把客户端放到Android真机上去了,导致socket一直连接不到服务器,刚开始还以为代码有问题,后面把客户端服务器代码都放到电脑上运行,发现效果良好,测试成功。
另外这里要指出一点就是实际上客户端服务器的通信是通过OutputStream来进行的,下面对这点作一些说明。
代码中我们通过pw.write方法来发送数据
pw.write("this is my username and password");
这个方法会调用到PrintWriter.dowrite方法中去
private final void doWrite(char[] buf, int offset, int count) {
synchronized (lock) {
if (out != null) {
try {
out.write(buf, offset, count);
} catch (IOException e) {
setError();
}
} else {
setError();
}
}
}
而这里的
out.write(buf, offset, count);
这个代码中的out变量,实际上就是
OutputStream os = socket.getOutputStream();
这里的os变量,现在就明白了一点,我们能够通过socket.getOutputStream返回的OutputStream直接向服务器写数据。
而这里的os对象实际上是PlainSocketOutputStream类的实例,那么os.write的方法实际上是调用到了
PlainSocketOutputStream.write方法中
@Override public void write(int oneByte) throws IOException {
Streams.writeSingleByte(this, oneByte);
}
这里的Streams是libcore.io.Streams,是比较深层的东西了,这里暂时不深究。
3、参考文献
1、[Java Socket编程----通信是这样炼成的](https://www.cnblogs.com/rocomp/p/4790340.html)
这篇参考文献,讲的关于Socket通信的知识点还蛮多的,感兴趣的童鞋可以去看看。