大家在学习计算机网络的时候一定学习过 TCP/IP 协议以及最经典的 OSI 七层结构,简单的回忆一下这 7 层结构:
从下到上依次是:

TCP/IP 协议对这 7 层了做一点精简,变为了 4 层结构:

我们现在的网路通信模型基本上都是按照这个层级来分发的,当然也包括了 Android 中的网络模型,简单回顾一下基础之后,开始学习今天的网络接口——Socket。
在模型之下,又衍生出两种经典的传输层协议——TCP 和 UDP,我们分别看看这两个协议。
TCP 协议是传输控制协议是一个面向连接的协议,所谓的面向连接表示的是通信双方在传输数据之前,需要搭建一个专用的通信线路,并且在结束的时候需要将其关闭。在有了这条专用线路作保障之后,就能准确无误的将数据传递给对方,所以 TCP 是一种可靠的通信方式,它能够准确知道对方是否成功接收了消息。
UDP 又叫用户数据包协议,相对于 TCP,它是一种面向无连接的协议,也就是通信双方在交换数据之前无需建立一条专用通道,当然在通信结束前也无需释放通道。这样一来,通信的效率非常高,但缺点是我们无法确定发出去的消息对方是否能够准确收到,所以它是一个轻量不可靠的通信方式。
以上的定义描述了二者主要的差异,更多细致的内容可以参考其他资料。
Socket 翻译成中文是“套接字”,它是应用层和传输层中间的一个抽象中间件,它封装了底层的 TCP / IP 协议族,并向上暴露 API 给应用层,从而向上屏蔽底层协议细节。 所以 Socket 可以作为底层网络门面来让我们不在拘泥于复杂的底层传输协议,而将更多的重心放在自己的功能开发上。 下图是 Socket 的一个整体工作原理:

每个 Socket 对象都对应着一个 IP 地址和一个端口号,用来标识互联网上的唯一目的地址,然后就可以通过 TCP 或者 UDP 将数据发送给对方。
首先看看 Socket 的工作流程图:

首先由服务端初始化 Socket 接口,然后绑定并监听自己的端口号,此时服务端会阻塞式等待客户端连接。
客户端可以在需要发送消息的时候初始化 Socket 接口,设置服务端的 IP 地址和端口号就可以连接到服务器,接着在连接成功之后,双方就完成了连接的建立。
在连接建立好之后,客户端或者服务端双方就可以开始发送数据了,在数据传输完毕之后,双方任一方都可以申请断开连接,此后通道关闭,数据传输完成。
public ServerSocket(int port) throws IOException
创建 Socket 服务只需要传入一个端口号即可。
public Socket accept() throws IOException
通过调用accept()方法,server 便会阻塞式的监听第 1 步设置的端口号,等待客户端连接。
public Socket(String host, int port) throws UnknownHostException, IOException
和前面说的一样,创建 Socket 需要两个必要的参数:
socket.getInputStream();
socket.getOutputStream();
服务端和客户端通过这两个方法分别拿到输入输出流,从而向流里面写消息或者从流里面读数据完成数据的发送和接收。
close()方法断开连接,完成本次通信。本节在电脑上通过 Java 搭建一个 Socket Server,然后手机作为 Client 来连接 Server。这个需要保证手机和电脑在同一个 Wifi 网段下。
大家在学习 Android 之前,应该都有学过纯 Java 程序,可以通过javac命令来将 java 代码编译成字节码,然后通过java命令运行,当然也可以在 Android Studio 里面直接运行带main()方法的 Java 程序。
首先在工程里新建一个 Java Library,注意不是 Android Library。

然后创建一个带main()函数的类,在里面完成 Socket 的创建和初始化:
package com.emercy.libsocket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
public class SocketServer {
public static void main(String[] args) throws IOException {
// 1. Create ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
// 2. monitoring
System.out.println("server start listen : " + getIpAddress());
Socket socket = serverSocket.accept();
System.out.println("accept");
// 3. input stream
InputStream is = socket.getInputStream();
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
String content;
StringBuffer sb = new StringBuffer();
while ((content = br.readLine()) != null) {
sb.append(content);
}
System.out.println("server receiver: " + sb.toString());
socket.shutdownInput();
br.close();
reader.close();
is.close();
socket.close();
serverSocket.close();
System.out.println("server receiver: ");
}
public static String getIpAddress() {
try {
Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
InetAddress ip = null;
while (allNetInterfaces.hasMoreElements()) {
NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
continue;
} else {
Enumeration<InetAddress> addresses = netInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
ip = addresses.nextElement();
if (ip instanceof Inet4Address) {
return ip.getHostAddress();
}
}
}
}
} catch (Exception e) {
System.err.println("IP地址获取失败" + e.toString());
}
return "";
}
}
运行之后,开始等待连接并打印当前设备的 IP 地址,这里要特别注意,有些教程里面会用InetAddress.getLocalHost()这种方式获取 IP 返回“127.0.0.1”,这个是 local host,在跨设备通信中无法使用。
接下来编写 Android 程序,xml 里面只放置一个 Button 用于触发连接,这里就不列出来了。点击 button 之后按照上面的步骤来依次创建 Socket,设置 IP 和 port,接着获取输入输出流即可。
**注意:**网络请求属于耗时操作, Android 要求网络请求必须在子线程中执行,所以我们需要在onClick()中 new 一个 thread。
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//1. Create Client
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Socket socket;
try {
//1. create socket
socket = new Socket("10.64.210.51", 12345);
//2. output stream
OutputStream os = socket.getOutputStream();
//3. Send data
os.write("Hello world".getBytes());
System.out.println("send message");
os.flush();
socket.shutdownOutput();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
});
}
}
首先运行 Server,接着打开 App,点击“连接”Button 就可以和 Server 通信了。
本节学习了一个底层的网络接口——Socket,它内部实现了计算机网络中最基础的协议和模型,可以让我们不再关心那些繁琐复杂的协议规则,从而轻松的将数据传输出去。首先给大家回顾了 IOS 和 TCP / IP 的几层模型,然后工作在传输层的两个重要通信协议 TCP / UDP,接着按照步骤来分别创建 SocketServer 和 Socket,通过 InputStream 和 OutputStream 进行通信。
0/1000



















