题目描述
利用数据报通信方式编写一程序,该程序生成两个客户端,一个服务器端,两个客户端可以相互进行简短的文字交流。
实现思路
为使客户端能够接收到其他客户端的数据报,客户端在创建时应该指定端口号,开启接收数据的线程和等待发送消息的线程,当接收到数据或发送数据后,重启线程。并且发送方在数据报中应包含接收端的地址,由服务器端对地址进行解析并转达数据包。
实现过程
1. 服务器端:
在Server.java中实现Server类,静态变量指定服务器端口,接收数据报的大小
public class Server {
static int serverPort = 8000;
static int packetSize = 1024;
进入主函数时,初始化数据报套接字及接收数据报
public static void main(String[] args) throws Exception {
DatagramSocket server = new DatagramSocket(serverPort);
byte[] data = new byte[packetSize];
DatagramPacket packet = new DatagramPacket(data, packetSize);
System.out.println("server is ready!!");
使用死循环处理每一个客户端发送的数据报;
将数据报分割为地址及信息两部分;
根据地址及信息创建新数据报,并发送到目标客户端,完成对消息的转述
while (true) {
server.receive(packet);
System.out.println("server has received data from sender");
String dataStr = new String(data, 0, packet.getLength());
String[] dataSegments = dataStr.split(" ");
if(dataSegments.length >= 2){
InetAddress receiverAddress = InetAddress.getByName(dataSegments[0].split(":")[0]);
int receiverPort = Integer.parseInt(dataSegments[0].split(":")[1]);
DatagramPacket trulyData = new DatagramPacket(dataSegments[1].getBytes(),
dataSegments[1].length(), receiverAddress, receiverPort);
System.out.println("the message: "+dataSegments[1]);
server.send(trulyData);
System.out.println("server has sent data to receiver");
}
}
2. 客户端
客户端在创建时需指定端口号,这里使用命令行参数传入
public class Client {
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
DatagramSocket socket = new DatagramSocket(port);
System.out.println("client is ready!!");
创建接收消息的线程及发送消息的线程等待输入
// create a receiver thread
Thread receiveThread = new Thread(new ReceiverRunnable(socket));
receiveThread.start(); // receive message at any time
// create a sender thread
Thread sendThread = new Thread(new ClientSenderRunnable(socket));
sendThread.start();
}else{
System.out.println("you must indicate the port by adding argument");
}
}
}
(1)接收消息的线程
在Receiver.java中实现一个实现Runnable接口的可执行类
在构造方法中传入客户端数据报套接字,初始化接收端
在静态变量中定义数据报大小,以及接收数据报的字符数组
public class ReceiverRunnable implements Runnable {
static int dataSize = 1024;
DatagramPacket receivePacket;
byte[] data;
public DatagramSocket socket;
public ReceiverRunnable(DatagramSocket socket){
this.socket = socket;
data = new byte[dataSize];
receivePacket = new DatagramPacket(data, dataSize);
}
重写run方法,接收数据报并打印消息
在线程终点启动下一个接收消息的线程
@Override
public void run(){
try{
socket.receive(receivePacket);
System.out.println("You have received a message: "+new String(data, dataSize));
Thread thread = new Thread(new ReceiverRunnable(socket));
thread.start();
}catch (IOException e){
e.fillInStackTrace();
}
}
}
(2)发送消息的线程
在ClientSenderRunnable.java中实现一个实现Runnable接口的可执行类
在构造方法中传入客户端数据报套接字,初始化发送端
在静态变量中指定服务器地址及端口,用于初始化数据报
public class ClientSenderRunnable implements Runnable{
static String serverName = "localhost";
static int serverPort = 8000;
public DatagramSocket socket;
public ClientSenderRunnable(DatagramSocket socket){
this.socket = socket;
}
重写run方法,读取键盘输入
@Override
public void run() {
System.out.println("type as (address:port message):");
Scanner scanner = new Scanner(System.in);
String dataStr;
if (scanner.hasNextLine()) {
dataStr = scanner.nextLine();
System.out.println("your header and message: " + dataStr);
byte[] data = dataStr.getBytes();
根据静态变量指定的服务端地址打包数据报并发送
// pack up the data
try {
InetAddress serverAddress = InetAddress.getByName(serverName);
DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, serverPort);
// create socket and send data packed
socket.send(packet);
System.out.println("client has sent data to server!!");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
在线程终点启动下一个发送消息的线程
Thread thread = new Thread(new ClientSenderRunnable(socket));
thread.start();
}
}
运行结果
启动服务端
启动两个客户端
由客户端一(8001端口)向客户端二(8002端口)发送消息
服务器端成功接收到消息并转发
客户端二(8002端口)成功监听到接收的消息
同理,由客户端二向客户端一发送消息也同样得以完成
客户端二:
服务器端:
客户端一: