先确定一个服务端,服务端要有固定的IP和端口用来接受数据,且服务端IP要已知
例如,目前已知一个服务端IP为192.168.137.20,端口10025
服务端与客户端是一对多的关系
第零步
在客户端向服务端发送数据之前,我们需要先给服务端注册一个监听器,用来监听是否有客户端发来数据
步骤
先来封装一个类,专门用来向客户端发送数据和接收客户端发来的数据
向客户端发送数据我们先不讲,因为此时我们并不知道客户端的IP地址和端口号,所以只能先注册一个监听器,当有客户端向服务端发送数据且服务端收到后,服务端就可以知道客户端的IP地址和端口号了,这时就可以对客户端发送数据进行回复了,所以我们先来看怎么注册监听器。
SendToClient
public class SendToClient {
private DatagramSocket serverSocket = new DatagramSocket(10025);//创建一个socket,端口号为10025(上文假设),即监听自己的10025这个端口号是否收到数据
public SendToClient() throws SocketException {
}
/*
* 该函数主要用来向客户端发送数据,详细讲解见下文
*/
public void sendDataToClient(StringBuilder str,SocketAddress socketAddress) {
......
}
/*
* 该函数为注册监听器
*/
public void ReceiveDataFromClient() {
try {
while (true) { //开启一个死循环,不断的查看是否有客户端发来数据
byte data[] = new byte[8 * 1024];//创建一个足够长的字节来作为接收到的数据的容器
//创建一个packet,用来接收数据
DatagramPacket packet = new DatagramPacket(data, data.length);
serverSocket.receive(packet);//接收信息(如果有客户端发送过来)
/*将收到的信息转换成String,使用split是因为发来的数据可能包含多个数据,
在客户端我使用#来拼接起来了,
例如,客户端要给服务端发送一个名字和分数 ,
则客户端发送的数据为 乎如#90,
这样服务端收到后使用split("#")就可以将名字和分数分离了*/
String[] result = new String(packet.getData(),
packet.getOffset(), packet.getLength()).split("#");
//下面是对收到的数据的处理
if (!TextUtils.isEmpty(result[0])) {//如果收到的数据不为空
//TODO: 处理收到的数据
//注意,在这里,我们不仅收到了客户端发来的数据,同时也知道了客户端的IP地址和端口号
//通过 packet.getSocketAddress() 便可以获取到客户端的IP地址和端口号,具体用法见下文
}
}
} catch (SocketException e) {
e.printStackTrace();
Log.e("######","SocketException"+e);
} catch (IOException e) {
e.printStackTrace();
Log.e("######","IOException"+e);
}
}
//关闭socket,释放资源
public void disconnect(){
serverSocket.close();
serverSocket.disconnect();
}
}
用法
/*
*封装到一个函数中
*/
private void registerServerUDP(){
try {
sendToClient = new SendToClient();//实例化SendToClient类
//Log.e("######ServerService","已实例化");
} catch (SocketException e) {
e.printStackTrace();
//Log.e("######ServerService","非法的ip地址");
}
//开启一个线程,死循环不能开在主线程中,而且所有的网络操作都需要在子线程中
new Thread(new Runnable() {
@Override
public void run() {
//调用ReceiveDataFromClient()函数,开启监听器
sendToClient.ReceiveDataFromClient();
//Log.e("######ServerService","已创建服务器接收器");
}
}).start();
}
这样服务端的监听器就建好了,当有客户端向服务端发送数据时,服务端就能够收到,并进行相应的处理了。
第一步:
接下来,客户端就可以向服务端发送数据了
因为服务端IP和端口已知,且服务端已经建好了监听器,所以可以向服务端发送数据了,而现在服务端无法向客户端发送数据,因为我们现在是不知道客户端的IP和端口号的。
步骤:
首先,封装一个类,专门用来 向服务端发送数据和接受从服务端发过来的数据。
SendToServer
public class SendToServer {
private DatagramSocket clientSocket = new DatagramSocket();//创建一个socket用来向服务端发送数据
public SendToServer() throws SocketException {
}
/**
* 向服务端发送数据 str:要发送的数据 IP:服务端IP地址
*/
public void sendDataToServer(StringBuilder str,String IP) {
try {
InetAddress serverAddress = InetAddress.getByName(IP);//这个IP是已知的,即服务端IP,192.168.137.20(上文假设)
byte data[] = str.toString().getBytes();//将发送的数据转换成字节
DatagramPacket packetToServer = new DatagramPacket(data, data.length ,serverAddress,10025);//将上述数据封装到一个packet中,然后才可以用socket发送出去
clientSocket.send(packetToServer);//向服务端的10025(上文假设)发送数据
} catch (SocketException e) {
e.printStackTrace();
Log.e("######SendToServer","SocketException"+e);
} catch (UnknownHostException e) {
e.printStackTrace();
Log.e("######SendToServer","UnknownHostException"+e);
} catch (IOException e) {
e.printStackTrace();
Log.e("######SendToServer","IOException"+e);
}
}
/*
* 接受服务端发来的数据,注册客户端监听器,原理和上述服务端注册监听器相同
*/
public void ReceiveDataFromServer() {
try {
while(true) {//开启一个死循环,不断的查看是否有客户端发来数据
byte fromServer[] = new byte[4 * 1024];//创建一个足够长的字节来作为接收到的数据的容器
//创建一个packet,用来接收数据
DatagramPacket packet = new DatagramPacket(fromServer, fromServer.length);
clientSocket.receive(packet);//接收信息(如果有客户端发送过来)
//将收到的信息转换成String
String result = new String(packet.getData(), packet.getOffset(), packet.getLength());
//下面是对收到的数据的处理
if (!TextUtils.isEmpty(result)) {//如果收到的数据不为空
//TODO: 处理收到的数据
//在这里,我们不仅可以收到服务端发来的数据,同时也可以知道服务端的IP地址和端口号
//其实就是192.168.137.20(开头假设)和10025(开头假设)
//同样是通过packet.getSocketAddress()来获取
}
//Log.e("######SendToServer", "收到服务器返回的信息为:" + result);
}
} catch (SocketException e) {
e.printStackTrace();
Log.e("######SendToServer","SocketException"+e);
} catch (IOException e) {
e.printStackTrace();
Log.e("######SendToServer","IOException"+e);
}
}
//关闭socket,释放资源
public void disconnect(){
clientSocket.close();
clientSocket.disconnect();
}
}
用法:
/*
* 实例化并注册客户端监听器
*/
private void registerClientUDP(){
try {
sendToServer = new SendToServer();//实例化SendToServer类
Log.e("######GameActivity","已实例化");
} catch (SocketException e) {
e.printStackTrace();
//Log.e("######GameActivity","非法的ip地址");
}
//此处为注册客户端监听器,原理同服务端注册监听器相同
//开启一个线程,死循环不能开在主线程中,而且所有的网络操作都需要在子线程中
new Thread(new Runnable() {
@Override
public void run() {
//调用ReceiveDataFromClient()函数,开启监听器
sendToServer.ReceiveDataFromServer();
Log.e("######GameActivity","已创建客户端接收器");
}
}).start();
}
private void sendMessageToServer(final StringBuilder s){//封装成一个函数
new Thread(new Runnable() {//开启一个线程来发送数据(所有的网络请求都需要在子线程执行)
@Override
public void run() {
sendToServer.sendDataToServer(s,serverIP);//调用我们封装好的类里的函数,传入两个参数,s:要发送的数据,serverIP:服务端IP
}
}).start();
}
变量 s 类型我设置成了StringBuilder,在使用中完全可以设置成String
这样我们就向服务端发送了一条数据。
第二步
服务端接受客户端发来的数据,同时回复客户端
在SendToClient类里,上文中有一个类的代码没有贴出来,代码如下:
SendToClient里的sendDataToClient()函数
/*
* 这个类即服务端向客户端发送数据的函数,
* 注意这个函数和客户端向服务端发送数据那个函数的不同
* 传入参数由String IP变成了SocketAddress socketAddress
* 这是因为服务端收到客户端发来的数据后,通过packet.getSocketAddress()就可以获取到一个SocketAddress对象
* 该对象存储了客户端的IP地址和端口号,在SendToServer类的sendDataToServer函数里
* 我们通过DatagramPacket packetToServer = new DatagramPacket(data, data.length ,serverAddress,10025);
* 这句话创建了一个服务端的packet对象,通过socket(客户端的)和packet(指向服务端)发送给了服务端
* 但是在这个函数里,我们可以直接使用packet.getSocketAddress()来创建客户端的packet(指向客户端)
* 通过socket(服务端)和packet(指向客户端)来向客户端发送数据了
*/
public void sendDataToClient(StringBuilder str,SocketAddress socketAddress) {
try {
byte data[] = str.toString().getBytes();//讲要发送的数据转换成字节
//通过传进来的参数直接创建一个packet
DatagramPacket packetToClient = new DatagramPacket(data, data.length ,socketAddress);
//通过socket向客户端发送数据
serverSocket.send(packetToClient);
} catch (SocketException e) {
e.printStackTrace();
Log.e("######","SocketException"+e);
} catch (UnknownHostException e) {
e.printStackTrace();
Log.e("######","UnknownHostException"+e);
} catch (IOException e) {
e.printStackTrace();
Log.e("######","IOException"+e);
}
}
用法
private void sendMessageToServer(final StringBuilder s,SocketAddress socketAddress){//封装成一个函数
new Thread(new Runnable() {//开启一个线程来发送数据(所有的网络请求都需要在子线程执行)
@Override
public void run() {
sendToClient.sendDataToClient(s,socketAddress);//调用我们封装好的类里的函数,传入两个参数,s:要发送的数据,SocketAddress :接收到的客户端的数据
}
}).start();
}
这样,服务端就成功回复客户端了
第三步
客户端接收服务端发来的数据
在第一步中我们也已经建好了客户端的监听器,当收到数据时,监听器就会监听到,然后就可以对收到的数据进行处理了
SendToServer里的ReceiveDataFromServer()函数
/*
* 接受服务端发来的数据,注册客户端监听器,原理和上述服务端注册监听器相同
*/
public void ReceiveDataFromServer() {
try {
while(true) {//开启一个死循环,不断的查看是否有客户端发来数据
byte fromServer[] = new byte[4 * 1024];//创建一个足够长的字节来作为接收到的数据的容器
//创建一个packet,用来接收数据
DatagramPacket packet = new DatagramPacket(fromServer, fromServer.length);
clientSocket.receive(packet);//接收信息(如果有客户端发送过来)
//将收到的信息转换成String
String result = new String(packet.getData(), packet.getOffset(), packet.getLength());
//下面是对收到的数据的处理
if (!TextUtils.isEmpty(result)) {//如果收到的数据不为空
//TODO: 处理收到的数据
//在这里,我们不仅可以收到服务端发来的数据,同时也可以知道服务端的IP地址和端口号
//其实就是192.168.137.20(开头假设)和10025(开头假设)
//同样是通过packet.getSocketAddress()来获取
}
//Log.e("######SendToServer", "收到服务器返回的信息为:" + result);
}
} catch (SocketException e) {
e.printStackTrace();
Log.e("######SendToServer","SocketException"+e);
} catch (IOException e) {
e.printStackTrace();
Log.e("######SendToServer","IOException"+e);
}
}
最后
1、客户端向服务端发送数据
2、服务端接收数据
3、服务端回复客户端
4、客户端接收数据
不断的这样循环,就完成了不同设备下的UDP通讯了,另外了解一下UDP的特点
1、相比于TCP,传输前不需要建立连接,知道IP就可以直接发送数据
2、传输数据快,占用内存小
3、尽最大速度传输数据,但是并不能保证数据的正确性
以及,服务端和客户端需要在同一个局域网下才可以噢!
to my dear 琪琪.