Demo — IPConnect项目
IPConnect是一个基于ServerSocket实现的局域网通信Android项目,IPConnect启动后可以作为Server服务端,其他同一局域网内的设备Client可以通过服务端的IP地址及端口号与Server端进行连接通信
功能展示
-
项目启动后左列包含了当前服务端设备IP地址及端口号,端口号支持自定义
-
可以点击Start按钮来启动服务,服务启动后即可等待客户端的连接
-
任何与服务端在同一局域网内的设备都可以使用TCP客户端工具与服务器进行通信
-
服务端能够实时收到客户端发出的消息
-
一个服务端可以同时被多个客户端连接,每个客户端可以通过IP地址进行区分
使用到的技术点
- ServerSocket
- Get the wired/wireless network IP address
- TCP / IP
- RecyclerView
- MVC
Demo Apk:
项目源码地址:
ServerSocket实现局域网通信关键逻辑
ServerSocket使用主要步骤:
- 创建ServerSocket实例并绑定到指定端口:
// port为端口号
ServerSocket mServerSocket = new ServerSocket(port)
- 调用accept方法等待客户端连接:
Socket clientSocket = mServerSocket.accept();
- 与连接的客户端进行数据通信
// 通过输入流接收客户端数据
DataInputStream mIn = new DataInputStream(clientSocket.getInputStream());
int recvLen = mIn.read(new byte[1024]);
byte[] recvData = new byte[recvLen];
System.arraycopy(recvBuf, 0, recvData, 0, recvLen);
Log.d(TAG, "recvCmd=" + recvData);
// 通过输出流发送数据到客户端
DataInputStream mOut = new DataOutputStream(clientSocket.getOutputStream());
mOut.write(data, 0, data.length);
- 关闭连接
mServerSocket.close();
整体流程代码参考:
// 开启服务后执行如下Runnable
private class RunServer implements Runnable {
private final int port;
RunServer(int port) {
this.port = port;
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void run() {
if (port == -1) {
Log.e(TAG, "RunServer port ERROR:" + port);
updateServerInfo(false, port);
} else {
Log.d(TAG, "RunServer: run");
TrafficStats.setThreadStatsTag((int)Thread.currentThread().getId());
// 开启Socket
openSocket(port);
// 进入循环等待客户端连接
while (mServerSocket != null) {
try {
if (mSessions.size() < MAX_SESSION_NUM) {
Log.d(TAG, "RunServer: waiting ...");
// 等待客户端连接
Socket clientSocket = mServerSocket.accept();
if (clientSocket != null && clientSocket.getInetAddress() != null) {
// 连接到客户端
Session session = new Session(clientSocket, mSocketServer);
// 放到子线程处理数据交换
mExecutor.execute(session);
}
}
} catch (IOException e) {
Log.e(TAG, "RunServer: ERROR:", e);
}
}
}
}
}
// 启动Socket服务
private boolean openSocket(int port) {
// 启动前先确保关闭已启动的Socket
closeSocket();
try {
// 创建ServerSocket实例并绑定到指定端口
mServerSocket = new ServerSocket(port);
if (!mServerSocket.isClosed())
// 启动成功
return true;
else
// 启动失败
Log.d(TAG, "openSocket->failed!");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
// 关闭Socket服务
private void closeSocket() {
try {
if (mServerSocket != null) {
mServerSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
mServerSocket = null;
}
注意事项:
- 应该在后台线程处理网络操作,避免阻塞UI线程
- 服务端应该为每个客户端连接起一个新的子线程处理数据交换逻辑,以免阻塞其他客户端请求
- 添加必要的网络权限:
// 访问网络状态权限:获取IP地址需要
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
// 网络权限
<uses-permission android:name="android.permission.INTERNET" />
- 获取IP地址以及当前网络类型
public class NetworkUtil {
public static String getIpAddress(Context context) {
ConnectivityManager netManager = (ConnectivityManager) context.getApplicationContext().getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = netManager.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getType() == ConnectivityManager.TYPE_WIFI) {
return getWifiIpAddress(context);
} else if (info.getType() == ConnectivityManager.TYPE_ETHERNET) {
return getEthIpAddress();
}
}
return "0.0.0.0";
}
private static String getEthIpAddress() {
String infaceName = "eth0";
String ip = "0.0.0.0";
try {
Enumeration<NetworkInterface> netInterface = NetworkInterface.getNetworkInterfaces();
while (netInterface.hasMoreElements()) {
NetworkInterface inface = netInterface.nextElement();
if (!inface.isUp() || !infaceName.equals(inface.getDisplayName())) {
continue;
}
Enumeration<InetAddress> netAddressList = inface.getInetAddresses();
while (netAddressList.hasMoreElements()) {
InetAddress inetAddress = netAddressList.nextElement();
if (inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ip;
}
private static String getWifiIpAddress(Context context) {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
return String.format("%d.%d.%d.%d",
(ipAddress & 0xff),
(ipAddress >> 8 & 0xff),
(ipAddress >> 16 & 0xff),
(ipAddress >> 24 & 0xff));
}
public static String getNetworkType(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
if (cm != null) {
Network network = cm.getActiveNetwork();
if (network != null) {
NetworkCapabilities nc = cm.getNetworkCapabilities(network);
if (nc != null) {
if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return "WIFI";
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
return "ETHERNET";
}
}
}
}
return "UNKNOWN";
}
}