本来公司想做不经过服务器转发,两个设备进行P2P通信的, 经过调研发现好像只有电信的4G可以实现打洞, 移动和联通都不能直接进行P2P。然后就写了个demo测试一下udp通信。查询网上的资料都是已经知道客户端和服务端(其实UDP没有服务端之说)的地址直接进行通信,而我要进行的是两个客户端事先不知道对方的ip和port,那应该怎么通信呢?肯定要有一个中间者去将它们联系起来啊, 具体的做法是:
一个服务端S, 一个客户端A, 一个客户端B
A去登陆服务器,这时服务器可以得到A的地址和端口, 并保存下来
B也去登陆服务器,同样服务器保存了B的地址和端口
B登陆.jpg
这时A去获取在线的设备,服务器会把在线的ip和port返回,A就有了B的信息,这时就可以给B发送消息了
A向B发消息.jpg
B接收到A的消息.jpg B发送消息给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, 并监听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)
/**
* 发送数据
*/
fun sendSocketData(packs: String)
/**
* 发生错误
*/
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();
}
}
}
demo地址: https://pan.baidu.com/s/1PlhVU60QGH0ViwvcqHEuaQ 提取码: deus
网友评论