本來公司想做不經過服務器轉發(fā),兩個設備進行P2P通信的, 經過調研發(fā)現好像只有電信的4G可以實現打洞, 移動和聯通都不能直接進行P2P。然后就寫了個demo測試一下udp通信。查詢網上的資料都是已經知道客戶端和服務端(其實UDP沒有服務端之說)的地址直接進行通信,而我要進行的是兩個客戶端事先不知道對方的ip和port,那應該怎么通信呢?肯定要有一個中間者去將它們聯系起來啊, 具體的做法是:
一個服務端S, 一個客戶端A, 一個客戶端B
A去登陸服務器,這時服務器可以得到A的地址和端口, 并保存下來

A登陸.jpg
B也去登陸服務器,同樣服務器保存了B的地址和端口

B登陸.jpg
這時A去獲取在線的設備,服務器會把在線的ip和port返回,A就有了B的信息,這時就可以給B發(fā)送消息了

A向B發(fā)消息.jpg

B接收到A的消息.jpg

B發(fā)送消息給A.jpg

A收到B的消息.jpg
客戶端代碼:
class UdpThread : Thread(), Runnable {
//定義一個socket
private lateinit var socket: DatagramSocket
//服務器的地址和端口號
private var serverAddress = "192.168.1.17"
private var serverPort = 9000
//需要進行通信的客戶端ip和port
private var clientIP = ""
private var clientPort = 1
private var socketListener: SocketListener? = null
init {
try {
//初始化一個socket, 并監(jiān)聽6000端口
socket = DatagramSocket(6000)
Log.d("socket", "init socket")
} catch (e: SocketException) {
e.printStackTrace()
Log.d("socket", e.message)
}
}
fun setSocketListener(socketListener: SocketListener) {
this.socketListener = socketListener
}
fun sendPackData(messageBean: MessageBean, address: String, port: Int) {
val packs = Gson().toJson(messageBean).toByteArray()
try {
socket.send(DatagramPacket(packs, packs.size, InetAddress.getByName(address), port))
socketListener?.sendSocketData(messageBean.toString())
} catch (io: IOException) {
io.printStackTrace()
Log.d("socket", io.message)
socketListener?.error(io)
}
}
override fun run() {
super.run()
Log.d("socket", "start run")
try {
while (true) {
//接收數據
val receiveBytes = ByteArray(1024)
val packet = DatagramPacket(receiveBytes, receiveBytes.size)
socket.receive(packet)
//解析數據
val receiveData = packet.data
val json = String(Utils.subBytes(receiveData, 0, packet.length))
val messageBean = Gson().fromJson(json, MessageBean::class.java)
when (messageBean.protocol) {
//登陸結果
Cons.PROTOCOL_LOGIN_ACK -> socketListener?.receiveSocketData(messageBean.data)
//獲取在線設備結果
Cons.PROTOCOL_GET_ONLINE_ACK -> socketListener?.receiveSocketData(messageBean.data)
//請求連接結果
Cons.PROTOCOL_CONNECT_ACK -> socketListener?.receiveSocketData(messageBean.data)
else -> socketListener?.receiveSocketData(messageBean.data)
}
}
} catch (se: SocketException) {
se.printStackTrace()
Log.d("socket", se.message)
socketListener?.error(se)
} catch (io: IOException) {
io.printStackTrace()
Log.d("socket", io.message)
socketListener?.error(io)
} finally {
Log.d("socket", "close")
socket.close()
}
}
interface SocketListener {
/**
* 接收到的數據
*/
fun receiveSocketData(socketData: String)
/**
* 發(fā)送數據
*/
fun sendSocketData(packs: String)
/**
* 發(fā)生錯誤
*/
fun error(e: Throwable)
}
}
服務端代碼:
public class UdpServerThread extends Thread implements Runnable {
// 數據報套接字
private DatagramSocket datagramSocket;
// 用以接收數據報
private DatagramPacket datagramPacket;
private Map<String, String> loginMap = new HashMap<>();
public UdpServerThread() {
try {
datagramSocket = new DatagramSocket(9000);
System.out.println("Server Start and listenering 9000");
} catch (SocketException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
@Override
public void run() {
super.run();
try {
while (true){
//接收數據包
byte[] receiveData = new byte[1024];
datagramPacket = new DatagramPacket(receiveData, receiveData.length);
datagramSocket.receive(datagramPacket);
String ip = ((InetSocketAddress)datagramPacket.getSocketAddress()).getAddress().getHostAddress();
int port = datagramPacket.getPort();
byte[] datas = datagramPacket.getData();
String json = new String(Utils.subBytes(datas, 0, datagramPacket.getLength()));
System.out.println(json);
MessageBean messageBean = new Gson().fromJson(json, MessageBean.class);
//登陸
if (messageBean.getProtocol() == Cons.PROTOCOL_LOGIN){
boolean isLogin = false;
for (Map.Entry<String, String> entry : loginMap.entrySet()) {
if (Integer.valueOf(entry.getKey()) == messageBean.getId()){
isLogin = true;
break;
}
}
if (!isLogin){
//保存登陸信息
loginMap.put(String.valueOf(messageBean.getId()), ip + ":" + port);
String packs = new Gson().toJson(new MessageBean(0, Cons.PROTOCOL_LOGIN_ACK, "login success:" + messageBean.getId()));
sendPacketData(packs.getBytes(), ip, port);
}else{
String packs = new Gson().toJson(new MessageBean(0, Cons.PROTOCOL_LOGIN_ACK, "already login:" + messageBean.getId()));
sendPacketData(packs.getBytes(), ip, port);
}
}else if (messageBean.getProtocol() == Cons.PROTOCOL_GET_ONLINE){
String clients = Utils.getMapToString(loginMap);
String packts = new Gson().toJson(new MessageBean(0, Cons.PROTOCOL_GET_ONLINE_ACK, clients));
sendPacketData(packts.getBytes(), ip, port);
}
}
}catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關閉socket
if (datagramSocket != null) {
datagramSocket.close();
}
}
}
private void sendPacketData(byte[] packs, String address, int port) {
try {
datagramSocket.send(new DatagramPacket(packs, packs.length, InetAddress.getByName(address), port));
} catch (IOException e) {
e.printStackTrace();
}
}
}