Android TCP 协议 Socket 通信
Socket
中文套接字 ,用来描述 IP
地址和端口好,是通讯链的句柄,应用程序可以通过 Socket
向网络发送请求或者应答请求
Socket 是支持 TCP/IP
协议的网络通讯的基本操作单元,是对网络通讯过程中端点的抽象表示,包含了进行通讯所必须的五个基本信息:连接网络使用的协议,本地主机的 IP 地址,本地进程的端口号,远程主机的 IP 地址和远程主机上进程的端口号
Socket 通信模型
-
创建
ServerSocket
和Socket
-
打开连接到的
Socket
的输入/输出流 -
按照协议对
Socket
进行读/写操作 -
关闭输入输出流,以及
Socket
我们接下来写一个简单的例子,开启服务端后,客户端点击按钮然后链接服务端,
并向服务端发送一串字符串,表示通过 Socket
链接上服务器
Socket 服务端
服务器端创建一个网络监听的流程如下
-
创建
ServerSocket
对象,绑定监听的端口 -
调用
accept()
方法监听客户端的请求 -
连接建立后,通过输入流读取客户端发送的请求信息
-
通过输出流向客户端发送响应信息
-
关闭相关资源
范例
在某个目录下创建 TcpServer.java
,比如 d:\dev
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.Enumeration; public class TcpServer { public static String getServAddr() { try { Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces(); while (allNetInterfaces.hasMoreElements()) { NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement(); // 去除回环接口,子接口,未运行和接口 if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) { continue; } Enumeration<InetAddress> addresses = netInterface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress ip = addresses.nextElement(); if (ip != null) { // ipv4 if (ip instanceof Inet4Address) { if (ip.getHostAddress().startsWith("192") || ip.getHostAddress().startsWith("10") || ip.getHostAddress().startsWith("172") || ip.getHostAddress().startsWith("169")) { return ip.getHostAddress(); } } } } } } catch (SocketException e) { System.err.println("Error when getting host ip address"+ e.getMessage()); } return ""; } public static void main(String[] args) throws IOException { //1.创建一个服务器端 Socket,即 ServerSocket, 指定绑定的端口,并监听此端口 ServerSocket serverSocket = new ServerSocket(12345); System.out.println("服务器已经运行在" + getServAddr() + ":12345 ,等待客户端发送数据"); while ( true ) { //2.调用accept()等待客户端连接 Socket socket = serverSocket.accept(); //3.连接后获取输入流,读取客户端信息 InputStream is=null; InputStreamReader isr=null; BufferedReader br=null; OutputStream os=null; PrintWriter pw=null; is = socket.getInputStream(); //获取输入流 isr = new InputStreamReader(is,"UTF-8"); br = new BufferedReader(isr); String info = null; while((info=br.readLine())!=null){//循环读取客户端的信息 System.out.println("客户端发送过来的信息" + info); } socket.shutdownInput();//关闭输入流 } } }
打开终端或命令行提示符,输入以下命令运行 TcpServer.java
javac TcpServer.java && java TcpServer
如果出现下面的文字则说明服务正常启动
服务器已经运行在 192.168.0.108:12345 ,等待客户端发送数据
你的 ip 可能不同
然后客户端就可以通过 192.168.0.108:12345
访问我们的 UDP Server 了
Socket 客户端
Android APP 端创建一个链接到网络服务器的套接字的流程如下
-
创建
Socket
对象,指明需要链接的服务器的地址和端号 -
链接建立后,通过输出流向服务器发送请求信息
-
通过输出流获取服务器响应的信息
-
关闭相关资源
范例
-
创建一个 空的 Android 项目
cn.twle.android.TcpSocket
-
修改
AndroidManifest.xml
添加网络权限<uses-permission android:name="android.permission.INTERNET"/>
-
修改
activity_main.xml
添加一个 TextView 用来显示信息<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" android:orientation="vertical"> <TextView android:id="@+id/ms_msg" android:layout_height="match_parent" android:layout_width="match_parent" android:text="" /> </LinearLayout>
-
修改 MainActivity.java
package cn.twle.android.tcpsocket; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; import java.io.OutputStream; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; public class MainActivity extends AppCompatActivity { private TextView ms_msg; private StringBuilder sb; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch ( msg.what) { case 0x1001: ms_msg.setText(sb.toString()); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView ms_msg = (TextView) findViewById(R.id.ms_msg); new Thread(new Runnable(){ @Override public void run(){ try { //1.创建客户端Socket,指定服务器地址和端口 Socket socket = new Socket("192.168.0.108", 12345); //2.获取输出流,向服务器端发送信息 OutputStream os = socket.getOutputStream();//字节输出流 PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流 //获取客户端的IP地址 InetAddress address = InetAddress.getLocalHost(); String ip = address.getHostAddress(); pw.write(">>> 客户端:~" + ip + "~ 接入服务器!!"); pw.flush(); socket.shutdownOutput();//关闭输出流 socket.close(); } catch (Exception e ) { e.printStackTrace(); } } }).start(); } }
因为 Android
不允许在主线程 (UI线程) 中做网络操作,需要我们自己另开一个线程来连接 Socket
运行之后我们的 APP 输出如下
再看一下服务器端,输出
服务器已经运行在192.168.0.108:12345 ,等待客户端发送数据 客户端发送过来的信息>>> 客户端:~127.0.0.1~ 接入服务器!!
说明服务器端已经接收到了数据