现有2台手机,小米和魅族。魅族分享热点给小米用,魅族做服务端,小米做客户端。
一开始我以为要获取2台手机的ip地址,但是后来发现不用。
ip
地址获取是通过DhcpInfo
去获取的。ipAddress
代表本机ip
地址,gateway
代表网关地址,即本机设备所连接的路由器ip
地址。
WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE);
DhcpInfo info = manager.getDhcpInfo();
Log.d(TAG,"gateway " + formatIP(info.gateway));
Log.d(TAG,"ipAddress " + formatIP(info.ipAddress));
private String formatIP(int ip) {
return String.format(
"%d.%d.%d.%d",
(ip & 0xff),
(ip >> 8 & 0xff),
(ip >> 16 & 0xff),
(ip >> 24 & 0xff)
);
}
并且要给权限。
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
魅族ip地址为192.168.43.1
小米ip地址为192.168.43.213
由于小米手机连接的是魅族手机分享的热点,因此小米手机连接网络的路由器地址就是192.168.43.1
。
要在2台手机上进行UDP
通讯的话,,由于魅族做服务端,因此需要192.168.43.1
这个地址。
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
Android默认热点分享的ip是192.168.43.1
,源码里写死了的,所以这里可以我直接写死而不是通过动态获取赋值。在UDP
通讯过程中,只需要一个ip
地址,那就是192.168.43.1
。
下面新建2个工程,service
和client
。注意通讯端口要保持一致,这里我都选择了3000
。
service
代码如下
首先加权限:
<uses-permission android:name="android.permission.INTERNET" />
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int SERVER_PORT = 3000;
private byte bufServer[] = new byte[1024];
private static final int BUF_LENGTH = 1024;
private boolean mIsRunning;
private DatagramSocket mDatagramSocket;
private DatagramPacket mDatagramPacket;
private TextView mTextView;
private Thread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text_view);
createServer();
}
private void createServer() {
mIsRunning = true;
try {
mDatagramSocket = new DatagramSocket(SERVER_PORT);
mDatagramPacket = new DatagramPacket(bufServer, BUF_LENGTH);
startServerThread();
} catch (SocketException e) {
e.printStackTrace();
}
}
private void startServerThread() {
mThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (mIsRunning) {
mDatagramSocket.receive(mDatagramPacket);
final String result = new String(mDatagramPacket.getData(), mDatagramPacket.getOffset(), mDatagramPacket.getLength());
Log.d(TAG,"result " + result);
// 每次接收完UDP数据后,重置长度。否则可能会导致下次收到数据包被截断。
if (mDatagramPacket != null) {
mDatagramPacket.setLength(BUF_LENGTH);
}
mTextView.post(new Runnable() {
@Override
public void run() {
mTextView.setText(result);
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
mThread.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
mIsRunning = false;
mDatagramSocket.close();
}
}
client
代码如下。
首先,加权限
<uses-permission android:name="android.permission.INTERNET" />
public class HeartbeatTimer {
private Timer timer;
private TimerTask task;
private OnScheduleListener mListener;
public HeartbeatTimer() {
timer = new Timer();
}
public void startTimer(long delay, long period) {
task = new TimerTask() {
@Override
public void run() {
if (mListener != null) {
mListener.onSchedule();
}
}
};
timer.schedule(task, delay, period);
}
public void exit() {
if (timer != null) {
timer.cancel();
}
if (task != null) {
task.cancel();
}
}
public interface OnScheduleListener {
void onSchedule();
}
public void setOnScheduleListener(OnScheduleListener listener) {
this.mListener = listener;
}
}
public class UDPSocket {
private static final String TAG = "UDPSocket";
private static final String SERVICE_IP = "192.168.43.1";
// 单个CPU线程池大小
private static final int POOL_SIZE = 5;
public static final int CLIENT_PORT = 3000;
private ExecutorService mThreadPool;
private DatagramSocket mDatagramSocket;
private HeartbeatTimer mHeartbeatTimer;
private long lastReceiveTime = 0;
private static final long TIME_OUT = 120 * 1000;
private static final long HEARTBEAT_MESSAGE_DURATION = 10 * 1000;
public UDPSocket() {
int cpuNumbers = Runtime.getRuntime().availableProcessors();
// 根据CPU数目初始化线程池
mThreadPool = Executors.newFixedThreadPool(cpuNumbers * POOL_SIZE);
// 记录创建对象时的时间
lastReceiveTime = System.currentTimeMillis();
}
public void startUDPSocket() {
if (mDatagramSocket != null) return;
try {
// 表明这个 Socket 在设置的端口上监听数据。
mDatagramSocket = new DatagramSocket(CLIENT_PORT);
startHeartbeatTimer();
} catch (SocketException e) {
e.printStackTrace();
}
}
public void stopUDPSocket() {
if (mDatagramSocket != null) {
mDatagramSocket.close();
mDatagramSocket = null;
}
if (mHeartbeatTimer != null) {
mHeartbeatTimer.exit();
}
}
/**
* 启动心跳,timer 间隔十秒
*/
private void startHeartbeatTimer() {
mHeartbeatTimer = new HeartbeatTimer();
mHeartbeatTimer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() {
@Override
public void onSchedule() {
Log.d(TAG, "timer is onSchedule...");
long duration = System.currentTimeMillis() - lastReceiveTime;
Log.d(TAG, "duration:" + duration);
if (duration > TIME_OUT) {//若超过两分钟都没收到我的心跳包,则认为对方不在线。
Log.d(TAG, "超时,对方已经下线");
// 刷新时间,重新进入下一个心跳周期
lastReceiveTime = System.currentTimeMillis();
} else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超过十秒他没收到我的心跳包,则重新发一个。
String string = "hello,this is a heartbeat message";
sendMessage(string);
}
}
});
mHeartbeatTimer.startTimer(0, 1000 * 10);
}
/**
* 发送心跳包
*
* @param message
*/
public void sendMessage(final String message) {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
InetAddress targetAddress = InetAddress.getByName(SERVICE_IP);
byte[] sendData = createSendData(message);
//创建用来发送的 DatagramPacket 数据报,其中应该包含要发送的信息,以及本地地址,目标端口号。
DatagramPacket packet = new DatagramPacket(sendData, sendData.length, targetAddress, CLIENT_PORT);
String result = new String(packet.getData(), 0, packet.getLength());
Log.d(TAG,"result " + result);
mDatagramSocket.send(packet);
// 数据发送事件
Log.d(TAG, "数据发送成功");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
//中文字符发送前要处理,否则会出现乱码,因此这里统一处理
private byte[] createSendData(String strSend) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dataStream = new DataOutputStream(baos);
try {
dataStream.writeUTF(strSend);
dataStream.close();
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return new byte[0];
}
}
public class MainActivity extends AppCompatActivity {
private EditText mEditText;
private Button mButton;
private UDPSocket mUDPSocket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = findViewById(R.id.edit_text);
mButton = findViewById(R.id.send_button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mUDPSocket.sendMessage(mEditText.getText().toString());
}
});
mUDPSocket = new UDPSocket();
mUDPSocket.startUDPSocket();
}
@Override
protected void onDestroy() {
super.onDestroy();
mUDPSocket.stopUDPSocket();
}
}
参考链接:
Android 网络编程(1)——Socket编程So easy
Android利用UDP、TCP进行局域网数据传输
Android基于UDP的局域网聊天通信(有完整Demo)
在 Android 上,一个完整的 UDP 通信模块应该是怎样的?
Socket 通信之 UDP 通信
Android UDP
网友评论