Java網(wǎng)絡(luò)編程之UDP實(shí)現(xiàn)原理解析
UDP實(shí)現(xiàn)通信非常簡(jiǎn)單,沒有服務(wù)器,每個(gè)都是客戶端,每個(gè)客戶端都需要一個(gè)發(fā)送端口和一個(gè)接收端口。一個(gè)客戶端向另一個(gè)客戶端發(fā)送消息時(shí),需要知道對(duì)方的IP和接收端口,所用到的類為DatagramSocket。
DatagramSocket socket =new DatagramSocket(),發(fā)送端socket,若不指定端口,系統(tǒng)自動(dòng)分配
DatagramSocket socket =new DatagramSocket("接收信息端口"),接收端socket,需要指定接收端口
若想客戶端之間進(jìn)行全雙工通信,每個(gè)客戶端都要有兩個(gè)線程,一個(gè)用于發(fā)送信息,一個(gè)用于接收信息。
那么UDP怎么實(shí)現(xiàn)私聊和群聊呢?(在本機(jī)一臺(tái)電腦的情況下實(shí)現(xiàn))
首先私聊,客戶端向另一個(gè)客戶端發(fā)送消息,就要知道其IP(本機(jī)都是固定的localhost)和接收端口,也需要姓名進(jìn)行標(biāo)識(shí),所以,每個(gè)客戶端都至少要自己的姓名和接收端口,而且端口不可重復(fù),否則會(huì)報(bào)端口被占用的錯(cuò)。
其次群聊,由于在本機(jī)一臺(tái)電腦上進(jìn)行,接收端口各不相同,所以廣播就不行了,此時(shí)就希望每個(gè)客戶端在啟動(dòng)的時(shí)候,能夠把自己的姓名和接收端口給存起來(lái),然后就可以遍歷進(jìn)行群聊。
實(shí)現(xiàn):
- 第一種,在每個(gè)客戶端啟動(dòng)時(shí),輸入自己的姓名和接收端口,發(fā)送信息時(shí),需要輸入對(duì)方的接收端口號(hào),如果輸入時(shí)輸入了多個(gè)端口,就是群發(fā)。那么這樣每次發(fā)送信息時(shí)都要指定對(duì)方的端口。。。
- 第二種,客戶端啟動(dòng)時(shí),輸入姓名和接收端口,此時(shí)就把數(shù)據(jù)存起來(lái),發(fā)送信息時(shí),只用指定對(duì)方姓名即可。。??捎脭?shù)據(jù)庫(kù)存,可用文件存,我用的是XML來(lái)存。
要?jiǎng)?chuàng)建xml文件,路徑在Operation類中
UdpClient.java:
public class UdpClient {
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
User user = new User();
System.out.print("請(qǐng)輸入用戶名》》");
String userName = scanner.next();
if (Operation.userIsExist(userName)) {
//如果此用戶已經(jīng)注冊(cè)過,直接把注冊(cè)時(shí)用的接收端口分配給他
user = Operation.findUserByName(userName);
}else {
//未注冊(cè),用戶自己指定端口
while(true) {
System.out.println("請(qǐng)輸入接收端口》》");
int port = Integer.parseInt(scanner.next());
if (Operation.portIsExist(port)) {
System.err.println("該端口已被使用,請(qǐng)重新輸入。。。。");
continue;
}else {
user.setName(userName);
user.setPort(port);
Operation.addUser(user);
break;
}
}
}
new Thread(new SendMsg(user)).start();
new Thread(new ReceiveMsg(user)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
發(fā)送信息:
public class SendMsg implements Runnable{
private User self = null;
private DatagramSocket socket = null;
private BufferedReader reader = null;
public SendMsg(User self) {
try {
socket = new DatagramSocket();
reader = new BufferedReader(new InputStreamReader(System.in));
this.self = self;
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while(true) {
String[] msg = reader.readLine().split("@");
if (msg.length != 2) {
System.err.println("注意格式:消息@對(duì)方名字(私聊)或all(群聊)");
continue;
}
msg[0] = self.getName()+"說:"+msg[0];
byte[] data = msg[0].getBytes();
String toPerson = msg[1];
if (("all").equals(toPerson)) {
//群聊,獲取所有用戶,不管對(duì)方在不在線,都發(fā)過去
List<User> users = Operation.getUsers();
for(User user:users) {
if (self != user) {
DatagramPacket packet = new DatagramPacket(data, 0,data.length,new InetSocketAddress("localhost",user.getPort()));
socket.send(packet);
}
}
}else {
//私聊
try {
DatagramPacket packet = new DatagramPacket(data, 0,data.length,new InetSocketAddress("localhost",Operation.findUserByName(toPerson).getPort()));
socket.send(packet);
} catch (Exception e) {
System.out.println("對(duì)方不在線。。。");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收消息:
public class ReceiveMsg implements Runnable{
private DatagramSocket socket = null;
public ReceiveMsg(User user) {
try {
socket = new DatagramSocket(user.getPort());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while(true) {
//準(zhǔn)備接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
socket.receive(packet);
byte[]data = packet.getData();
String receiveData = new String(data, 0, data.length);
System.out.println(receiveData);
}
} catch (Exception e) {
e.printStackTrace();
}
socket.close();
}
}
操作XML文件類:
public class Operation {
private static String FILE_PATH = "config/user.xml"; //文件目錄
//在xml文件中添加一個(gè)用戶信息
public static void addUser(User user)
{
InputStream in = null;
SAXReader reader = new SAXReader();
Document doc = null;
try
{
in = new FileInputStream(FILE_PATH);
doc = reader.read(in);
Element root = doc.getRootElement(); //獲取xml根節(jié)點(diǎn),即users節(jié)點(diǎn)
Element element = root.addElement("user");
element.addElement("name").addText(user.getName());
element.addElement("port").addText(String.valueOf(user.getPort()));
FileOutputStream fos = new FileOutputStream(FILE_PATH);
//格式化xml文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("utf-8");
XMLWriter writer = new XMLWriter(fos,format);
writer.write(doc);
writer.close();
}
catch (Exception e)
{
System.out.println("error");
}
finally
{
try
{
if(in != null)
in.close();
}
catch (IOException e)
{
System.out.println("error");
}
}
}
//列出xml中所有用戶信息
public static List<User> getUsers()
{
InputStream in = null;
SAXReader reader = new SAXReader();
Document doc = null;
List<User> users = new ArrayList<>();
try
{
in = new FileInputStream(FILE_PATH);
doc = reader.read(in);
Element root = doc.getRootElement();
List<Element> elements = root.elements();
for (Element element : elements)
{
User user = new User();
user.setName(element.elementText("name"));
user.setPort(Integer.valueOf(element.elementText("port")));
users.add(user);
}
}
catch (Exception e1)
{
System.out.println("error");
}
finally
{
try
{
in.close();
}
catch (IOException e)
{
System.out.println("error");
}
}
return users;
}
public static User findUserByName(String name) {
InputStream in = null;
SAXReader reader = new SAXReader();
Document doc = null;
try {
in = new FileInputStream(FILE_PATH);
doc = reader.read(in);
Element root = doc.getRootElement();
List<Element> elements = root.elements();
for (Element element : elements)
{
if(name != null && name.equals(element.elementText("name"))) {
User user = new User();
user.setName(name);
user.setPort(Integer.parseInt(element.elementText("port")));
return user;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}
public static boolean portIsExist(int port) {
InputStream in = null;
SAXReader reader = new SAXReader();
Document doc = null;
try {
in = new FileInputStream(FILE_PATH);
doc = reader.read(in);
Element root = doc.getRootElement();
List<Element> elements = root.elements();
for (Element element : elements)
{
if(port == Integer.parseInt(element.elementText("port")))
return true;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
return false;
}
//判斷某個(gè)用戶是否存在該xml中
public static boolean userIsExist(String name)
{
InputStream in = null;
SAXReader reader = new SAXReader();
Document doc = null;
try {
in = new FileInputStream(FILE_PATH);
doc = reader.read(in);
Element root = doc.getRootElement();
List<Element> elements = root.elements();
for (Element element : elements)
{
if(name != null && name.equals(element.elementText("name")))
return true;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
return false;
}
}
用戶實(shí)體類:
public class User implements Serializable{
private String name;//姓名
private int port;//接收端口
public String getName() {
return name;
}
public int getPort() {
return port;
}
public void setName(String name) {
this.name = name;
}
public void setPort(int port) {
this.port = port;
}
@Override
public String toString() {
return "User [name=" + name + ", port=" + port + "]";
}
}
運(yùn)行結(jié)果:


到此這篇關(guān)于Java網(wǎng)絡(luò)編程之UDP實(shí)現(xiàn)原理解析的文章就介紹到這了,更多相關(guān)Java網(wǎng)絡(luò)編程UDP內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java Socket實(shí)現(xiàn)UDP編程淺析
- Java網(wǎng)絡(luò)編程之UDP網(wǎng)絡(luò)通信詳解
- Java網(wǎng)絡(luò)編程UDP協(xié)議發(fā)送接收數(shù)據(jù)
- Java網(wǎng)絡(luò)編程UDP實(shí)現(xiàn)消息發(fā)送及聊天
- Java網(wǎng)絡(luò)編程UDP實(shí)現(xiàn)多線程在線聊天
- Java編程使用UDP建立群聊系統(tǒng)代碼實(shí)例
- JAVA編程實(shí)現(xiàn)UDP網(wǎng)絡(luò)通訊的方法示例
- Java實(shí)現(xiàn)基于UDP協(xié)議的網(wǎng)絡(luò)通信UDP編程
相關(guān)文章
Java中ShardingSphere 數(shù)據(jù)分片的實(shí)現(xiàn)
其實(shí)很多人對(duì)分庫(kù)分表多少都有點(diǎn)恐懼,我們今天用ShardingSphere 給大家演示數(shù)據(jù)分片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
自己動(dòng)手在Spring-Boot上加強(qiáng)國(guó)際化功能的示例
這篇文章主要介紹了自己動(dòng)手在Spring-Boot上加強(qiáng)國(guó)際化功能的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-04-04
mybatis-plus雪花算法增強(qiáng)idworker的實(shí)現(xiàn)
今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,進(jìn)一步增強(qiáng)實(shí)現(xiàn)生成分布式唯一ID,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
Maven指令打包SpringBoot項(xiàng)目提示沒有主清單文件問題
在Java開發(fā)中,打包Jar時(shí)常會(huì)遇到“沒有主清單屬性”的錯(cuò)誤,這通常是因?yàn)樵趐om.xml文件中沒有正確配置maven插件導(dǎo)致的,特別是在使用自定義的<parent/>節(jié)點(diǎn)而非spring-boot-starter-parent時(shí)2024-09-09
深入理解Spring MVC的數(shù)據(jù)轉(zhuǎn)換
這篇文章主要給大家介紹了關(guān)于Spring MVC數(shù)據(jù)轉(zhuǎn)換的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧。2017-09-09

