在实现Java聊天室之前,我们先了解一下什么是UDP协议,以及使用UDP协议的客户端或服务器端是如何来接收信息或者发送信息的。
UPD(User Datagram Protocol)用户数据报协议,是网络模型中的传输层协议。UDP协议是无连接、不可靠的,并且它是面向报文的,并不是像TCP一样,面向字节流的。UDP没有拥塞控制,适合媒体通信。
在Java.net报中,也提供了两个类DatagramSocket和DatagramPacket来支持UDP的数据报通信。DatagramSocket是用于在程序之间建立传输的通信通道,并且它发送的每个包都需要指定地址。而DatagramPacket是用来表示一个数据包的,DatagramPacket只是在首次创建时指定地址,以后所有的包都会通过DatagramSocket发送。
UDP编程分为四个部分:建立连接,发送数据,接收数据,关闭连接。
1、在服务器端:使用UDP来监听指定端口。代码如下:
DatagramSocket ds = new DatagramSocket(6666); // 监听指定端口
while (true) { // 无限循环
// 数据缓冲区:
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet); // 收取一个UDP数据包
// 收取到的数据存储在buffer中,由packet.getOffset(), packet.getLength()指定起始位置和长度
// 将其按UTF-8编码转换为String:
String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
// 发送数据:
byte[] data = "ACK".getBytes(StandardCharsets.UTF_8);
packet.setData(data);
ds.send(packet);
}
2、服务器首先使用如下语句在指定端口监听UDP数据包:
// 监听6666端口
DatagramSocket ds = new DatagramSocket(6666);
3、 要接收一个数据包,需要准备一个byte[] 类型数组缓冲区,并通过DatagramPacket实现接收:
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
4、如果我们收到的是String类型,则通过DatagramPacket返回的packet.getOfferset()和packet.getLength()确定数据在缓冲区的起止位置:
String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
5、当服务器收到一个UDP数据包后,通常会立刻回复一个UDP包:
byte[] data = ...
packet.setData(data);
ds.send(packet);
6、 而在客户端,只需要直接使用socket发送一个数据包,等待服务器响应一个数据包即可:
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666); // 连接指定服务器和端口
// 发送:
byte[] data = "Hello".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length);
ds.send(packet);
// 接收:
byte[] buffer = new byte[1024];
packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
String resp = new String(packet.getData(), packet.getOffset(), packet.getLength());
ds.disconnect();
7、客户端打开一个socket:
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666);
8、客户端实例时候,不需要指定端口,操作系统会指定一个当前未使用的端口,然后调用setSoTime(3000),设定超时时间。如果超时没有收到数据包,则会自动断开与当前服务器的连接。
9、如果客户端认为通信结束了,就可以调用disconnect()断开连接。disconnect()方法只是清楚了发送到服务器端的IP和端口。以便于socket连接下一台服务器。
下面展示基于UDP协议的Java聊天室:
代码展示(服务器端 or 客户端1):
package com.apesource;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import javax.xml.crypto.Data;
public class ChatA {
public static void main(String[] args) {
// 控制台上输入
Scanner scan = new Scanner(System.in);
// 创建DatagramSocket的对象,自身端口为1097
try (DatagramSocket socket = new DatagramSocket(1097)) {
// 创建DatagramPacket对象,同时监控客户端8888端口
DatagramPacket sendPacket = new DatagramPacket(new byte[1024], 1024,new InetSocketAddress("192.168.254.146", 8888));
DatagramPacket receivePacket = new DatagramPacket(new byte[1024],1024);
while(true) {
// 发送
System.out.print("服务器端说:");
String str = scan.nextLine();
if(str.equals("退出聊天")) {
String res = "对方已经退出聊天室!";
byte[] sendContent = res.getBytes();
sendPacket.setData(sendContent);
socket.send(sendPacket);
break;
}
// 接收
socket.receive(receivePacket);
String receiveContent = new String(receivePacket.getData(),receivePacket.getOffset(),receivePacket.getLength());
System.out.print("客户端说:" + receiveContent);
System.out.println();
}
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
代码展示(服务器端 or 客户端2):
package com.apesource;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Scanner;
public class ChatB {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 自身端口8888
try (DatagramSocket socket = new DatagramSocket(8888)) {
DatagramPacket sendPacket = new DatagramPacket(new byte[1024], 1024,new InetSocketAddress("192.168.254.146", 1097));
DatagramPacket receivePacket = new DatagramPacket(new byte[1024],1024);
while(true) {
// 接收
socket.receive(receivePacket);
String receiveContent = new String(receivePacket.getData(),receivePacket.getOffset(),receivePacket.getLength());
System.out.print("服务器端说:" + receiveContent);
System.out.println();
// 发送
System.out.print("客户端说:");
String str = scan.nextLine();
if(str.equals("退出聊天")) {
String res = "对方已退出聊天室!";
byte[] sendContent = res.getBytes();
sendPacket.setData(sendContent);
socket.send(sendPacket);
break;
}
byte[] sendContent = str.getBytes();
sendPacket.setData(sendContent);
socket.send(sendPacket);
}
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
版权归原作者 卡多希y 所有, 如有侵权,请联系我们删除。