开发一个基于UDP协议的网络聊天室。
提示:
不使用广播实现。
客户端的接收消息和发送消息分别使用一个线程。
服务器端可不使用线程,维护一个ArrayList列表用来存储客户端的地址,当服务器端接收到消息后,先提取客户端的地址,和列表中存储的地址比较是否存在,如果存在,则接收消息后转发给列表中所有的客户端地址,如果不存在,则把该客户端地址提取出来保存在列表中,并接收消息转发给列表中的所有客户端地址。
基于UDP协议的网络编程通信原理和TCP协议不同,基于TCP协议的通信是客户端和服务器端建立连接管道,这个管道就像一个输出输出流,发送消息使用输出流,接收消息使用输入流;基于UDP协议的通信不建立连接,一端向另一端发送消息需要创建数据报,数据报中包含目标地址和要发送的内容。
服务器端收到后,提取出内容和客户端的地址,把消息再发回给客户端。
客户端代码中 finalSocket 与 finalSocket1 去接收socket, 而不是重新构造数据报套接字,是为了系统分配的端口一致,以便服务器能根据该端口发送接收信息给该客户端地址
客户端代码
package com.fan.udp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @Author: Travelmate
* @CreateTime: 2021/3/12 19:08
* @Description: 客户端
*/
// finalSocket 与 finalSocket1 去接收socket, 而不是重新构造数据报套接字,是为了系统分配的端口一致,以便服务器能根据该端口发送接收信息给该客户端地址
public class UdpClient {
public static void main(String[] args) {
//调用构造方法
new UdpClient();
}
public UdpClient(){
DatagramSocket socket = null;
try {
//端口使用系统分配的端口
socket = new DatagramSocket();
//
DatagramSocket finalSocket = socket;
//发送信息到服务器
new Thread(new Runnable() {
@Override
public void run() {
String userInput;
//接收键盘输入
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println("请输入用户名字: ");
String username = stdIn.readLine();
//服务器端地址
InetAddress ip = InetAddress.getByName("127.0.0.1");
//字节数组 存储发送内容
byte[] bytes = null;
DatagramPacket packet = null;
while ((userInput = stdIn.readLine())!=null){
bytes = (username + " : " + userInput).getBytes();
//服务器的目的地址,端口号,构造数据报
packet = new DatagramPacket(bytes, bytes.length, ip, 9999);
//发送数据报
finalSocket.send(packet);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
if (finalSocket != null){
finalSocket.close();
}
}
}
}).start();
DatagramSocket finalSocket1 = socket;
//接收服务器传来的信息
new Thread(new Runnable() {
@Override
public void run() {
DatagramPacket packet = null;
try {
byte[] bytes = new byte[1024];
while (true){
packet = new DatagramPacket(bytes, bytes.length);
finalSocket1.receive(packet);
String receiveStr = new String(packet.getData(), 0 , packet.getLength());
System.out.println(receiveStr);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
if (finalSocket1 != null){
finalSocket1.close();
}
}
}
}).start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
服务端代码
package com.fan.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
/**
* @Author: Travelmate
* @CreateTime: 2021/3/12 19:08
* @Description: 服务器端
*/
public class UdpServer {
public static void main(String[] args) {
DatagramSocket socket = null;
DatagramPacket packet = null;
//用来存储客户端的地址 我这里用(ip+端口号)
ArrayList<String> arrayList = new ArrayList<>();
String receive;
try {
//绑定端口9999,构造数据报
socket = new DatagramSocket(9999);
while (true){
byte[] buf = new byte[1024];
packet = new DatagramPacket(buf,buf.length);
//接收数据报
socket.receive(packet);
//提取客户端的地址 ip + 端口号
InetAddress inetAddress = packet.getAddress();
int port = packet.getPort();
//如果不存在 , 就加入
if (!arrayList.contains(inetAddress + ":" + port)){
arrayList.add(inetAddress + ":" + port);
}
receive = new String(packet.getData(), 0 , packet.getLength());
System.out.println(receive + " 来自:" + inetAddress + ":" + port);
byte[] bytes = new byte[1024];
bytes = ("来自客户端线程" + inetAddress + ":" + port + "的信息: " + receive).getBytes();
// 接收消息转发给列表中的所有客户端地址。
for (String s : arrayList){
// 去掉地址最前面的 / 字符
String str1 = s.substring(1);
String[] str = str1.split(":");
InetAddress ip = InetAddress.getByName(str[0]);
int port1 = Integer.parseInt(str[1]);
packet = new DatagramPacket(bytes, bytes.length , ip, port1);
socket.send(packet);
}
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (socket != null){
socket.close();
}
}
}
}
开启服务端和两个客户端 截图
服务端(接收所有客户端发送的信息)
客户端1(发送信息,接收服务端收到的所有信息记录)
客户端2(发送信息,接收服务端收到的所有信息记录)