本篇文章為大家展示了如何在java項目中使用MulticastSocket,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
創(chuàng)新互聯(lián)建站專注于玉泉企業(yè)網(wǎng)站建設,響應式網(wǎng)站建設,商城網(wǎng)站建設。玉泉網(wǎng)站建設公司,為玉泉等地區(qū)提供建站服務。全流程定制網(wǎng)站制作,專業(yè)設計,全程項目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務
(1)DatagramSocket只允許數(shù)據(jù)報發(fā)給指定的目標地址,而MulticastSocket可以將數(shù)據(jù)報以廣播的方式發(fā)送到多個客戶端。
(2)IP協(xié)議為多點廣播提供了這批特殊的IP地址,這些IP地址的范圍是:224.0.0.0至239.255.255.255..
(3)MulticastSocket類時實現(xiàn)多點廣播的關(guān)鍵,當MulticastSocket把一個DaragramPocket發(fā)送到多點廣播的IP地址時,該數(shù)據(jù)報將會自動廣播到加入該地址的所有MulticastSocket。MulticastSocket既可以將數(shù)據(jù)報發(fā)送到多點廣播地址,也可以接收其他主機的廣播信息。
(4)事實上,MulticastSocket是DatagramSocket的子類,也就是說,MulticastSocket是特殊的DatagramSocket。當要發(fā)送一個數(shù)據(jù)報時,可以使用隨機端口創(chuàng)建MulticastSocket,也可以在指定端口創(chuàng)建MulticastSocket。MulticastSocket提供了如下三個構(gòu)造器:
public MulticastSocket() 使用本機默認地址,隨機端口來創(chuàng)建MulticastSocket對象
public MulticastSocket(int portNumber) 用本機默認地址,指定端口來創(chuàng)建MulticastSocket對象
public MulticastSocket(SocketAddress bindaddr) 用指定IP地址,指定端口來創(chuàng)建MulticastSocket對象
(5)創(chuàng)建MulticastSocket對象后,還需要將MulticastSocket加入到指定的多點廣播地址。MulticastSocket使用joinGroup()方法加入指定組;使用leaveGroup()方法脫離一個組。
joinGroup(InetAddress multicastAddr) 將該MulticastSocket加入到指定的多點廣播地址
leaveGroup(InetAddress multicastAddr) 將該MulticastSocket離開指定的多點廣播地址
(6)在某些系統(tǒng)中,可能有多個網(wǎng)絡接口,這可能為多點廣播帶來問題,這時候程序需要在一個指定的網(wǎng)絡接口上監(jiān)聽,通過調(diào)用setInterface()方法可以強制MulticastSocket使用指定的網(wǎng)絡接口‘也可以使用getInterface()方法查詢MulticastSocket監(jiān)聽的網(wǎng)絡接口。
(7)如果創(chuàng)建僅僅用于發(fā)送數(shù)據(jù)報的MulticastSocket對象,則使用默認地址,隨機端口即可。但如果創(chuàng)建接收用的MulticastSocket對象,'則該MulticastSocket對象必須有指定端口,否則無法確定發(fā)送數(shù)據(jù)報的目標端口。
(8)MulticastSocket用于發(fā)送接收數(shù)據(jù)報的方法與DatagramSocket完全一樣。但MulticastSocket比DatagramSocket多了一個setTimeToLive(int ttl)方法,該ttl用于設置數(shù)據(jù)報最多可以跨過多少個網(wǎng)絡。
當ttl為0時,指定數(shù)據(jù)報應停留在本地主機
當ttl為1時,指定數(shù)據(jù)報發(fā)送到本地局域網(wǎng)
當ttl為32時,指定數(shù)據(jù)報發(fā)送到本站點的網(wǎng)絡上
當ttl為64時,意味著數(shù)據(jù)報應該停留在本地區(qū)
當ttl為128時,意味著數(shù)據(jù)報應保留在本大洲
當ttl為255時,意味著數(shù)據(jù)報可以發(fā)送到所有地方
默認情況下,ttl值為1.
程序?qū)嵗?/strong>
下面程序使用MulticastSocket實現(xiàn)一個基于廣播的多人聊天室。程序只需要一個MulticastSocket,兩個線程,其中MulticastSocket既用于發(fā)送,也用于接收;一個線程負責鍵盤輸入,并向MulticastSocket發(fā)送數(shù)據(jù);一個線程負責從MulticastSocket中讀取數(shù)據(jù)。
package com.talk; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.util.Scanner; //讓該類實現(xiàn)Runnable接口,該類的實例可以作為線程的target public class MulticastSocketTest implements Runnable{ //使用常量作為本程序多點廣播的IP地址 private static final String BROADCAST_IP="230.0.0.1"; //使用常量作為本程序的多點廣播的目的地端口 public static final int BROADCAST_PORT=3000; //定義每個數(shù)據(jù)報大小最大為4kb private static final int DATA_LEN=4096; //定義本程序的MulticastSocket實例 private MulticastSocket socket=null; private InetAddress broadcastAddress=null; private Scanner scan=null; //定義接收網(wǎng)絡數(shù)據(jù)的字節(jié)數(shù)組 byte[] inBuff=new byte[DATA_LEN]; //以指定字節(jié)數(shù)組創(chuàng)建準備接收數(shù)據(jù)的MulticastSocket對象 private DatagramPacket inPacket =new DatagramPacket(inBuff, inBuff.length); //定義一個用于發(fā)送的DatagramPacket對象 private DatagramPacket outPacket=null; public void init() throws IOException{ //創(chuàng)建鍵盤輸入流 Scanner scan=new Scanner(System.in); //創(chuàng)建用于發(fā)送、接收數(shù)據(jù)的MulticastSocket對象,由于該MulticastSocket需要接收數(shù)據(jù),所以有指定端口 socket=new MulticastSocket(BROADCAST_PORT); broadcastAddress=InetAddress.getByName(BROADCAST_IP); //將該socket加入到指定的多點廣播地址 socket.joinGroup(broadcastAddress); //設置本MulticastSocket發(fā)送的數(shù)據(jù)報會被回送到自身 socket.setLoopbackMode(false); //初始化發(fā)送用的DatagramSocket,它包含一個長度為0的字節(jié)數(shù)組 outPacket =new DatagramPacket(new byte[0], 0, broadcastAddress, BROADCAST_PORT); //啟動本實例的run()方法作為線程執(zhí)行體的線程 new Thread(this).start(); //不斷的讀取鍵盤輸入 while(scan.hasNextLine()){ //將鍵盤輸入的一行字符轉(zhuǎn)換成字節(jié)數(shù)組 byte [] buff=scan.nextLine().getBytes(); //設置發(fā)送用的DatagramPacket里的字節(jié)數(shù)據(jù) outPacket.setData(buff); //發(fā)送數(shù)據(jù)報 socket.send(outPacket); } socket.close(); } public void run() { // TODO Auto-generated method stub while(true){ //讀取Socket中的數(shù)據(jù),讀到的數(shù)據(jù)放入inPacket所封裝的字節(jié)組里 try { socket.receive(inPacket); //打印從socket讀取到的內(nèi)容 System.out.println("聊天信息:"+new String(inBuff,0,inPacket.getLength())); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(socket!=null){ //讓該socket離開多點IP廣播地址 try { socket.leaveGroup(broadcastAddress); //關(guān)閉socket對象 socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.exit(1); } } public static void main(String[] args) { try { new MulticastSocketTest().init(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
下面將結(jié)合MulticastSocket和DatagramSocket開發(fā)一個簡單的局域網(wǎng)即時通訊工具,局域網(wǎng)內(nèi)每個用戶啟動該工具后,就可以看到該局域網(wǎng)內(nèi)所有的在線用戶,該用戶也會被其他用戶看到:
該程序的思路是:每個用戶都啟動兩個Socket,即MulticastSocket和DatagramSocket。其中MulticastSocket會周期性的向230.0.0.1發(fā)送在線信息,且所有的MulticastSocket都會加入到230.0.0.1這個多點廣播IP中,這樣每個用戶都會收到其他用戶的在線信息,如果系統(tǒng)在一段時間內(nèi)沒有收到某個用戶廣播的在線信息,則從用戶列表中刪除該用戶。除此之外,該MulticastSocket還用于向其他用戶發(fā)送廣播信息。
DatagramSocket主要用于發(fā)送私聊信息,當用戶收到其他用戶廣播來的DatagramSocket時,即可獲得該用戶MulticastSocket對應的SocketAddress.這個SocketAddress將作為發(fā)送私聊信息的重要依據(jù)?!境绦蜃孧ulticastSocket在30000端口監(jiān)聽,而DatagramSocket在30001端口監(jiān)聽,這樣程序就可以根據(jù)其他用戶廣播來的DatagramPacket得到他的DatagramSocket所在的地址。
本系統(tǒng)提供了一個UserInfo類,該類封裝了用戶名、圖標、對應的SocketAddress以及該用戶對應的交談窗口,失去聯(lián)系的次數(shù)等信息:
package com.talk; import java.net.SocketAddress; import com.bank.ChatFrame; public class UserInfo { // 該用戶的圖標 private String icon; // 該用戶的名字 private String name; // 該用戶的MulitcastSocket所在的IP和端口 private SocketAddress address; // 該用戶失去聯(lián)系的次數(shù) private int lost; // 該用戶對應的交談窗口 private ChatFrame chatFrame; public UserInfo(){} // 有參數(shù)的構(gòu)造器 public UserInfo(String icon , String name , SocketAddress address , int lost) { this.icon = icon; this.name = name; this.address = address; this.lost = lost; } // 省略所有成員變量的setter和getter方法 // icon的setter和getter方法 public void setIcon(String icon) { this.icon = icon; } public String getIcon() { return this.icon; } // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // address的setter和getter方法 public void setAddress(SocketAddress address) { this.address = address; } public SocketAddress getAddress() { return this.address; } // lost的setter和getter方法 public void setLost(int lost) { this.lost = lost; } public int getLost() { return this.lost; } // chatFrame的setter和getter方法 public void setChatFrame(ChatFrame chatFrame) { this.chatFrame = chatFrame; } public ChatFrame getChatFrame() { return this.chatFrame; } // 使用address作為該用戶的標識,所以根據(jù)address作為 // 重寫hashCode()和equals方法的標準 public int hashCode() { return address.hashCode(); } public boolean equals(Object obj) { if (obj != null && obj.getClass() == UserInfo.class) { UserInfo target = (UserInfo)obj; if (address != null) { return address.equals(target.getAddress()); } } return false; } }
通過UserInfo的封裝,所有客戶端只需要維護該UserInfo類的列表,程序就可以實現(xiàn)廣播、發(fā)送私聊信息等功能。本程序的底層通信類則需要一個MulticastSocket和一個DatagramSocket,該工具類的代碼如下:
package com.talk; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.SocketAddress; import java.util.ArrayList; import javax.swing.JOptionPane; public class ComUtil { // 定義本程序通信所使用的字符集 public static final String CHARSET = "utf-8"; // 使用常量作為本程序的多點廣播IP地址 private static final String BROADCAST_IP = "230.0.0.1"; // 使用常量作為本程序的多點廣播目的的端口 // DatagramSocket所用的的端口為該端口+1。 public static final int BROADCAST_PORT = 30000; // 定義每個數(shù)據(jù)報的最大大小為4K private static final int DATA_LEN = 4096; // 定義本程序的MulticastSocket實例 private MulticastSocket socket = null; // 定義本程序私聊的Socket實例 private DatagramSocket singleSocket = null; // 定義廣播的IP地址 private InetAddress broadcastAddress = null; // 定義接收網(wǎng)絡數(shù)據(jù)的字節(jié)數(shù)組 byte[] inBuff = new byte[DATA_LEN]; // 以指定字節(jié)數(shù)組創(chuàng)建準備接受數(shù)據(jù)的DatagramPacket對象 private DatagramPacket inPacket = new DatagramPacket(inBuff , inBuff.length); // 定義一個用于發(fā)送的DatagramPacket對象 private DatagramPacket outPacket = null; // 聊天的主界面程序 private LanTalk lanTalk; // 構(gòu)造器,初始化資源 public ComUtil(LanTalk lanTalk) throws Exception { this.lanTalk = lanTalk; // 創(chuàng)建用于發(fā)送、接收數(shù)據(jù)的MulticastSocket對象 // 因為該MulticastSocket對象需要接收,所以有指定端口 socket = new MulticastSocket(BROADCAST_PORT); // 創(chuàng)建私聊用的DatagramSocket對象 singleSocket = new DatagramSocket(BROADCAST_PORT + 1); broadcastAddress = InetAddress.getByName(BROADCAST_IP); // 將該socket加入指定的多點廣播地址 socket.joinGroup(broadcastAddress); // 設置本MulticastSocket發(fā)送的數(shù)據(jù)報被回送到自身 socket.setLoopbackMode(false); // 初始化發(fā)送用的DatagramSocket,它包含一個長度為0的字節(jié)數(shù)組 outPacket = new DatagramPacket(new byte[0] , 0 , broadcastAddress , BROADCAST_PORT); // 啟動兩個讀取網(wǎng)絡數(shù)據(jù)的線程 new ReadBroad().start(); Thread.sleep(1); new ReadSingle().start(); } // 廣播消息的工具方法 public void broadCast(String msg) { try { // 將msg字符串轉(zhuǎn)換字節(jié)數(shù)組 byte[] buff = msg.getBytes(CHARSET); // 設置發(fā)送用的DatagramPacket里的字節(jié)數(shù)據(jù) outPacket.setData(buff); // 發(fā)送數(shù)據(jù)報 socket.send(outPacket); } // 捕捉異常 catch (IOException ex) { ex.printStackTrace(); if (socket != null) { // 關(guān)閉該Socket對象 socket.close(); } JOptionPane.showMessageDialog(null , "發(fā)送信息異常,請確認30000端口空閑,且網(wǎng)絡連接正常!" , "網(wǎng)絡異常", JOptionPane.ERROR_MESSAGE); System.exit(1); } } // 定義向單獨用戶發(fā)送消息的方法 public void sendSingle(String msg , SocketAddress dest) { try { // 將msg字符串轉(zhuǎn)換字節(jié)數(shù)組 byte[] buff = msg.getBytes(CHARSET); DatagramPacket packet = new DatagramPacket(buff , buff.length , dest); singleSocket.send(packet); } // 捕捉異常 catch (IOException ex) { ex.printStackTrace(); if (singleSocket != null) { // 關(guān)閉該Socket對象 singleSocket.close(); } JOptionPane.showMessageDialog(null , "發(fā)送信息異常,請確認30001端口空閑,且網(wǎng)絡連接正常!" , "網(wǎng)絡異常", JOptionPane.ERROR_MESSAGE); System.exit(1); } } // 不斷從DatagramSocket中讀取數(shù)據(jù)的線程 class ReadSingle extends Thread { // 定義接收網(wǎng)絡數(shù)據(jù)的字節(jié)數(shù)組 byte[] singleBuff = new byte[DATA_LEN]; private DatagramPacket singlePacket = new DatagramPacket(singleBuff , singleBuff.length); public void run() { while (true) { try { // 讀取Socket中的數(shù)據(jù)。 singleSocket.receive(singlePacket); // 處理讀到的信息 lanTalk.processMsg(singlePacket , true); } // 捕捉異常 catch (IOException ex) { ex.printStackTrace(); if (singleSocket != null) { // 關(guān)閉該Socket對象 singleSocket.close(); } JOptionPane.showMessageDialog(null , "接收信息異常,請確認30001端口空閑,且網(wǎng)絡連接正常!" , "網(wǎng)絡異常", JOptionPane.ERROR_MESSAGE); System.exit(1); } } } } // 持續(xù)讀取MulticastSocket的線程 class ReadBroad extends Thread { public void run() { while (true) { try { // 讀取Socket中的數(shù)據(jù)。 socket.receive(inPacket); // 打印輸出從socket中讀取的內(nèi)容 String msg = new String(inBuff , 0 , inPacket.getLength() , CHARSET); // 讀到的內(nèi)容是在線信息 if (msg.startsWith(YeekuProtocol.PRESENCE) && msg.endsWith(YeekuProtocol.PRESENCE)) { String userMsg = msg.substring(2 , msg.length() - 2); String[] userInfo = userMsg.split(YeekuProtocol .SPLITTER); UserInfo user = new UserInfo(userInfo[1] , userInfo[0] , inPacket.getSocketAddress(), 0); // 控制是否需要添加該用戶的旗標 boolean addFlag = true; ArrayListdelList = new ArrayList<>(); // 遍歷系統(tǒng)中已有的所有用戶,該循環(huán)必須循環(huán)完成 for (int i = 1 ; i < lanTalk.getUserNum() ; i++ ) { UserInfo current = lanTalk.getUser(i); // 將所有用戶失去聯(lián)系的次數(shù)加1 current.setLost(current.getLost() + 1); // 如果該信息由指定用戶發(fā)送過來 if (current.equals(user)) { current.setLost(0); // 設置該用戶無須添加 addFlag = false; } if (current.getLost() > 2) { delList.add(i); } } // 刪除delList中的所有索引對應的用戶 for (int i = 0; i < delList.size() ; i++) { lanTalk.removeUser(delList.get(i)); } if (addFlag) { // 添加新用戶 lanTalk.addUser(user); } } // 讀到的內(nèi)容是公聊信息 else { // 處理讀到的信息 lanTalk.processMsg(inPacket , false); } } // 捕捉異常 catch (IOException ex) { ex.printStackTrace(); if (socket != null) { // 關(guān)閉該Socket對象 socket.close(); } JOptionPane.showMessageDialog(null , "接收信息異常,請確認30000端口空閑,且網(wǎng)絡連接正常!" , "網(wǎng)絡異常", JOptionPane.ERROR_MESSAGE); System.exit(1); } } } } }
本程序的一個主類,LanTalk ,該類使用DefaultListModel來維護用戶列表,該類里的每個列表項就是一個UserInfo。該類還提供了一個ImageCellRenderer,該類用于將列表項繪制出用戶圖標和用戶名字。
package com.talk; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.DatagramPacket; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.text.DateFormat; import java.util.Date; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; import com.bank.ChatFrame; import com.bank.LoginFrame; public class LanTalk extends JFrame { private DefaultListModellistModel = new DefaultListModel<>(); // 定義一個JList對象 private JList friendsList = new JList<>(listModel); // 定義一個用于格式化日期的格式器 private DateFormat formatter = DateFormat.getDateTimeInstance(); public LanTalk() { super("局域網(wǎng)聊天"); // 設置該JList使用ImageCellRenderer作為單元格繪制器 friendsList.setCellRenderer(new ImageCellRenderer()); listModel.addElement(new UserInfo("all" , "所有人" , null , -2000)); friendsList.addMouseListener(new ChangeMusicListener()); add(new JScrollPane(friendsList)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(2, 2, 160 , 600); } // 根據(jù)地址來查詢用戶 public UserInfo getUserBySocketAddress(SocketAddress address) { for (int i = 1 ; i < getUserNum() ; i++) { UserInfo user = getUser(i); if (user.getAddress() != null && user.getAddress().equals(address)) { return user; } } return null; } // ------下面四個方法是對ListModel的包裝------ // 向用戶列表中添加用戶 public void addUser(UserInfo user) { listModel.addElement(user); } // 從用戶列表中刪除用戶 public void removeUser(int pos) { listModel.removeElementAt(pos); } // 獲取該聊天窗口的用戶數(shù)量 public int getUserNum() { return listModel.size(); } // 獲取指定位置的用戶 public UserInfo getUser(int pos) { return listModel.elementAt(pos); } // 實現(xiàn)JList上的鼠標雙擊事件的監(jiān)聽器 class ChangeMusicListener extends MouseAdapter { public void mouseClicked(MouseEvent e) { // 如果鼠標的擊鍵次數(shù)大于2 if (e.getClickCount() >= 2) { // 取出鼠標雙擊時選中的列表項 UserInfo user = (UserInfo)friendsList.getSelectedValue(); // 如果該列表項對應用戶的交談窗口為null if (user.getChatFrame() == null) { // 為該用戶創(chuàng)建一個交談窗口,并讓該用戶引用該窗口 user.setChatFrame(new ChatFrame(null , user)); } // 如果該用戶的窗口沒有顯示,則讓該用戶的窗口顯示出來 if (!user.getChatFrame().isShowing()) { user.getChatFrame().setVisible(true); } } } } /** * 處理網(wǎng)絡數(shù)據(jù)報,該方法將根據(jù)聊天信息得到聊天者, * 并將信息顯示在聊天對話框中。 * @param packet 需要處理的數(shù)據(jù)報 * @param single 該信息是否為私聊信息 */ public void processMsg(DatagramPacket packet , boolean single) { // 獲取該發(fā)送該數(shù)據(jù)報的SocketAddress InetSocketAddress srcAddress = (InetSocketAddress) packet.getSocketAddress(); // 如果是私聊信息,則該Packet獲取的是DatagramSocket的地址, // 將端口減1才是對應的MulticastSocket的地址 if (single) { srcAddress = new InetSocketAddress(srcAddress.getHostName() , srcAddress.getPort() - 1); } UserInfo srcUser = getUserBySocketAddress(srcAddress); if (srcUser != null) { // 確定消息將要顯示到哪個用戶對應窗口上。 UserInfo alertUser = single ? srcUser : getUser(0); // 如果該用戶對應的窗口為空,顯示該窗口 if (alertUser.getChatFrame() == null) { alertUser.setChatFrame(new ChatFrame(null , alertUser)); } // 定義添加的提示信息 String tipMsg = single ? "對您說:" : "對大家說:"; try{ // 顯示提示信息 alertUser.getChatFrame().addString(srcUser.getName() + tipMsg + "......................(" + formatter.format(new Date()) + ")\n" + new String(packet.getData() , 0 , packet.getLength() , ComUtil.CHARSET) + "\n"); } catch (Exception ex) { ex.printStackTrace(); } if (!alertUser.getChatFrame().isShowing()) { alertUser.getChatFrame().setVisible(true); } } } // 主方法,程序的入口 public static void main(String[] args) { LanTalk lanTalk = new LanTalk(); new LoginFrame(lanTalk , "請輸入用戶名、頭像后登錄"); } } // 定義用于改變JList列表項外觀的類 class ImageCellRenderer extends JPanel implements ListCellRenderer { private ImageIcon icon; private String name; // 定義繪制單元格時的背景色 private Color background; // 定義繪制單元格時的前景色 private Color foreground; @Override public Component getListCellRendererComponent(JList list , UserInfo userInfo , int index , boolean isSelected , boolean cellHasFocus) { // 設置圖標 icon = new ImageIcon("ico/" + userInfo.getIcon() + ".gif"); name = userInfo.getName(); // 設置背景色、前景色 background = isSelected ? list.getSelectionBackground() : list.getBackground(); foreground = isSelected ? list.getSelectionForeground() : list.getForeground(); // 返回該JPanel對象作為單元格繪制器 return this; } // 重寫paintComponent方法,改變JPanel的外觀 public void paintComponent(Graphics g) { int imageWidth = icon.getImage().getWidth(null); int imageHeight = icon.getImage().getHeight(null); g.setColor(background); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(foreground); // 繪制好友圖標 g.drawImage(icon.getImage() , getWidth() / 2 - imageWidth / 2 , 10 , null); g.setFont(new Font("SansSerif" , Font.BOLD , 18)); // 繪制好友用戶名 g.drawString(name, getWidth() / 2 - name.length() * 10 , imageHeight + 30 ); } // 通過該方法來設置該ImageCellRenderer的最佳大小 public Dimension getPreferredSize() { return new Dimension(60, 80); } }
除了以上主要的代碼,還有YeekuProtocol ChatFrame LoginFrame等類:
package com.talk; public interface YeekuProtocol { String PRESENCE = "⊿⊿"; String SPLITTER = "▓"; }
package com.bank; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import com.talk.ComUtil; import com.talk.LanTalk; import com.talk.YeekuProtocol; // 登錄用的對話框 public class LoginFrame extends JDialog { public JLabel tip; public JTextField userField = new JTextField("錢鐘書" , 20); public JComboBoxiconList = new JComboBox<>( new Integer[]{1, 2, 3, 4, 5 , 6, 7, 8 ,9 ,10}); private JButton loginBn = new JButton("登錄"); // 聊天的主界面 private LanTalk chatFrame; // 聊天通信的工具實例 public static ComUtil comUtil; // 構(gòu)造器,用于初始化的登錄對話框 public LoginFrame(LanTalk parent , String msg) { super(parent , "輸入名字后登錄" , true); this.chatFrame = parent; setLayout(new GridLayout(5, 1)); JPanel jp = new JPanel(); tip = new JLabel(msg); tip.setFont(new Font("Serif" , Font.BOLD , 16)); jp.add(tip); add(jp); add(getPanel("用戶名" , userField)); iconList.setPreferredSize(new Dimension(224, 20)); add(getPanel("圖 標" , iconList)); JPanel bp = new JPanel(); loginBn.addActionListener(new MyActionListener(this)); bp.add(loginBn); add(bp); pack(); setVisible(true); } // 工具方法,該方法將一個字符串和組件組合成JPanel對象 private JPanel getPanel(String name , JComponent jf) { JPanel jp = new JPanel(); jp.add(new JLabel(name + ":")); jp.add(jf); return jp; } // 該方法用于改變登錄窗口最上面的提示信息 public void setTipMsg(String tip) { this.tip.setText(tip); } // 定義一個事件監(jiān)聽器 class MyActionListener implements ActionListener { private LoginFrame loginFrame; public MyActionListener(LoginFrame loginFrame) { this.loginFrame = loginFrame; } // 當鼠標單擊事件發(fā)生時 public void actionPerformed(ActionEvent evt) { try { // 初始化聊天通信類 comUtil = new ComUtil(chatFrame); final String loginMsg = YeekuProtocol.PRESENCE + userField.getText() + YeekuProtocol.SPLITTER + iconList.getSelectedObjects()[0] + YeekuProtocol.PRESENCE; comUtil.broadCast(loginMsg); // 啟動定時器每20秒廣播一次在線信息 javax.swing.Timer timer = new javax.swing.Timer(1000 * 10 , event-> comUtil.broadCast(loginMsg)); timer.start(); loginFrame.setVisible(false); chatFrame.setVisible(true); } catch (Exception ex) { loginFrame.setTipMsg("確認30001端口空閑,且網(wǎng)絡正常!"); } } } }
package com.bank; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.net.InetSocketAddress; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.KeyStroke; import com.talk.LanTalk; import com.talk.UserInfo; // 定義交談的對話框 public class ChatFrame extends JDialog { // 聊天信息區(qū) JTextArea msgArea = new JTextArea(12 , 45); // 聊天輸入?yún)^(qū) JTextField chatField = new JTextField(30); // 發(fā)送聊天信息的按鈕 JButton sendBn = new JButton("發(fā)送"); // 該交談窗口對應的用戶 UserInfo user; // 構(gòu)造器,用于初始化交談對話框的界面 public ChatFrame(LanTalk parent , final UserInfo user) { super(parent , "和" + user.getName() + "聊天中" , false); this.user = user; msgArea.setEditable(false); add(new JScrollPane(msgArea)); JPanel buttom = new JPanel(); buttom.add(new JLabel("輸入信息:")); buttom.add(chatField); buttom.add(sendBn); add(buttom , BorderLayout.SOUTH); // 發(fā)送消息的Action,Action是ActionListener的子接口 Action sendAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { InetSocketAddress dest = (InetSocketAddress)user.getAddress(); // 在聊友列表中,所有人項的SocketAddress是null // 這表明是向所有人發(fā)送消息 if (dest == null) { LoginFrame.comUtil.broadCast(chatField.getText()); msgArea.setText("您對大家說:" + chatField.getText() + "\n" + msgArea.getText()); } // 向私人發(fā)送信息 else { // 獲取發(fā)送消息的目的 dest = new InetSocketAddress(dest.getHostName(), dest.getPort() + 1); LoginFrame.comUtil.sendSingle(chatField.getText(), dest); msgArea.setText("您對" + user.getName() + "說:" + chatField.getText() + "\n" + msgArea.getText()); } chatField.setText(""); } }; sendBn.addActionListener(sendAction); // 將Ctrl+Enter鍵和"send"關(guān)聯(lián) chatField.getInputMap().put(KeyStroke.getKeyStroke('\n' , java.awt.event.InputEvent.CTRL_MASK) , "send"); // 將"send"與sendAction關(guān)聯(lián) chatField.getActionMap().put("send", sendAction); pack(); } // 定義向聊天區(qū)域添加消息的方法 public void addString(String msg) { msgArea.setText(msg + "\n" + msgArea.getText()); } }
上述內(nèi)容就是如何在java項目中使用MulticastSocket,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。