Java網路程式設計之UDP
UDP實現通訊非常簡單,沒有伺服器,每個都是客戶端,每個客戶端都需要一個傳送埠和一個接收埠。一個客戶端向另一個客戶端傳送訊息時,需要知道對方的IP和接收埠,所用到的類為DatagramSocket。
DatagramSocket socket =new DatagramSocket(),傳送端socket,若不指定埠,系統自動分配
DatagramSocket socket =new DatagramSocket("接收資訊埠"),接收端socket,需要指定接收埠
若想客戶端之間進行全雙工通訊,每個客戶端都要有兩個執行緒,一個用於傳送資訊,一個用於接收資訊。
那麼UDP怎麼實現私聊和群聊呢?(在本機一臺電腦的情況下實現)
首先私聊,客戶端向另一個客戶端傳送訊息,就要知道其IP(本機都是固定的localhost)和接收埠,也需要姓名進行標識,所以,每個客戶端都至少要自己的姓名和接收埠,而且埠不可重複,否則會報埠被佔用的錯。
其次群聊,由於在本機一臺電腦上進行,接收埠各不相同,所以廣播就不行了,此時就希望每個客戶端在啟動的時候,能夠把自己的姓名和接收埠給存起來,然後就可以遍歷進行群聊。
實現:
-
第一種,在每個客戶端啟動時,輸入自己的姓名和接收埠,傳送資訊時,需要輸入對方的接收埠號,如果輸入時輸入了多個埠,就是群發。那麼這樣每次傳送資訊時都要指定對方的埠。。。
-
第二種,客戶端啟動時,輸入姓名和接收埠,此時就把資料存起來,傳送資訊時,只用指定對方姓名即可。。。可用資料庫存,可用檔案存,我用的是XML來存。
要建立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("請輸入使用者名稱》》");
String userName = scanner.next();
if (Operation.userIsExist(userName)) {
//如果此使用者已經註冊過,直接把註冊時用的接收埠分配給他
user = Operation.findUserByName(userName);
}else {
//未註冊,使用者自己指定埠
while(true) {
System.out.println("請輸入接收埠》》");
int port = Integer.parseInt(scanner.next());
if (Operation.portIsExist(port)) {
System.err.println("該埠已被使用,請重新輸入。。。。");
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();
}
}
}
傳送資訊:
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("注意格式:訊息@對方名字(私聊)或all(群聊)");
continue;
}
msg[0] = self.getName()+"說:"+msg[0];
byte[] data = msg[0].getBytes();
String toPerson = msg[1];
if (("all").equals(toPerson)) {
//群聊,獲取所有使用者,不管對方在不線上,都發過去
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("對方不線上。。。");
}
}
}
} 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) {
//準備接收包裹
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檔案中新增一個使用者資訊
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根節點,即users節點
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;
}
//判斷某個使用者是否存在該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;
}
}
使用者實體類:
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 + "]";
}
}
執行結果: