【Android】基于ServerSocket实现局域网通信(含Demo源码)

Demo — IPConnect项目

IPConnect是一个基于ServerSocket实现的局域网通信Android项目,IPConnect启动后可以作为Server服务端,其他同一局域网内的设备Client可以通过服务端的IP地址及端口号与Server端进行连接通信

功能展示

  1. 项目启动后左列包含了当前服务端设备IP地址及端口号,端口号支持自定义

  2. 可以点击Start按钮来启动服务,服务启动后即可等待客户端的连接
    IPConnect

  3. 任何与服务端在同一局域网内的设备都可以使用TCP客户端工具与服务器进行通信
    客户端连接服务端进行通信

  4. 服务端能够实时收到客户端发出的消息

  5. 一个服务端可以同时被多个客户端连接,每个客户端可以通过IP地址进行区分
    服务端接收到各客户端发出的数据

使用到的技术点

  • ServerSocket
  • Get the wired/wireless network IP address
  • TCP / IP
  • RecyclerView
  • MVC

Demo Apk:

IPConnect Demo apk

项目源码地址:

IPConnect项目地址

ServerSocket实现局域网通信关键逻辑

ServerSocket使用主要步骤:

  1. 创建ServerSocket实例并绑定到指定端口:
// port为端口号
ServerSocket mServerSocket = new ServerSocket(port)
  1. 调用accept方法等待客户端连接:
Socket clientSocket = mServerSocket.accept();
  1. 与连接的客户端进行数据通信
// 通过输入流接收客户端数据
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);
  1. 关闭连接
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;
}

注意事项:

  1. 应该在后台线程处理网络操作,避免阻塞UI线程
  2. 服务端应该为每个客户端连接起一个新的子线程处理数据交换逻辑,以免阻塞其他客户端请求
  3. 添加必要的网络权限:
// 访问网络状态权限:获取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" />
  1. 获取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";
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值