java 网络编程基础 UDP协议DatagramSocket,MulticastSocket 实现局域网聊天软件(Server + Client模式)
阅读原文时间:2023年07月09日阅读:2

开发思路:

服务端两个Socket:

  1. UDP的DatagramSocket接收客户端消息。
  2. 广播multicastSocket 发送广播消息

客户端一个Socket:

  1. multicastSocket 接收服务端发来的广播 (不允许客户端直接发广播),同时发送点对点数据到UDP服务端DatagramSocket 。
  2. javax.swing.* 提供客户端图形化界面

生疏知识点:

图形化界面按钮监控键盘

addActionListener(AbstractAction接口实现类);监听器。

**topTextField.getInputMap().put(KeyStroke.getKeyStroke('\n'),"confirm_username"); 键盘事件和名字关联;
**

topTextField.getActionMap().put("confirm_username",confirmListener); 事件别名和监听器关联

服务器端程序:

package tcpandudp.udpexample;

import java.io.IOException;
import java.net.*;

/**
* @ClassName MulticastServerScoket
* @projectName: object1
* @author: Zhangmingda
* @description: 设计思想:为避免所有人直接发广播,导致消息内容无环节把控,消息发送的时候,发送到非广播端口(作为一个服务端)再由此服务端向广播地址发送消息,
* date: 2021/5/13.
*/
public class MulticastServerScoket {
/**
* 广播Socket端口
*/
private static final int BROAD_PORT = 30000;
/**
* 广播IP地址 :224.0.0.0 到 239.255.255.255
*/
private static final String BROAD_IP = "230.3.3.3";
/**
* 服务端口
*/
private static final int SERVER_PORT = 8888;
private void start(){
try (
/**
* 广播用套接字
*/
MulticastSocket multicastSocket = new MulticastSocket(BROAD_PORT);
/**
* 服务端套接字
*/
DatagramSocket datagramSocket = new DatagramSocket(SERVER_PORT)
){
/**
* 广播IP地址
*/
InetAddress multi_addr = InetAddress.getByName(BROAD_IP);
/**
* 广播套接字配置接收广播的IP地址
*/
multicastSocket.joinGroup(multi_addr);
/**
* 构建接收UDP数据的包
*/
byte[] inBuff = new byte[4096];
DatagramPacket inPacket = new DatagramPacket(inBuff,inBuff.length);
/**
* 接收广播,并广播出去
*/
while (true){
//普通套接字收广播
datagramSocket.receive(inPacket);

            String content = new String(inPacket.getData(),0,inPacket.getLength());  
            System.out.println(content);  
            //广播套接字发广播  
            DatagramPacket outPacket = new DatagramPacket(inPacket.getData(),inPacket.getLength(),multi\_addr,BROAD\_PORT);  
            multicastSocket.send(outPacket);  
        }  
    } catch (UnknownHostException e) {  
        e.printStackTrace();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
}  
/\*\*  
 \* 程序入口  
 \*/  
public static void main(String\[\] args) {  
    new MulticastServerScoket().start();  
}  

}

客户端

package tcpandudp.udpexample;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicReference;

/**
* @ClassName MulticastClientSocket
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/5/13.
*/
public class MulticastClientSocket {
/**
* 广播Socket端口
*/
private static final int BROAD_PORT = 30000;
/**
* 广播IP地址 :224.0.0.0 到 239.255.255.255
*/
private static final String BROAD_IP = "230.3.3.3";

/\*\*  
 \* 要发送数据包的服务端IP地址和 端口  
 \*/  
private static final String SERVER\_IP = "127.0.0.1";  
private static final int    SERVER\_PORT = 8888;

private void go() {  
    JFrame jFrame = new JFrame("广播聊天客户端");  
    //上部  
    JPanel topPanel = new JPanel();  
    JTextField topTextField = new JTextField(30);  
    JButton topButton = new JButton("确认用户名");  
    topPanel.add(topTextField);  
    topPanel.add(topButton);  
    jFrame.add(topPanel, BorderLayout.NORTH);  
    //中部  
    JTextArea centerArea = new JTextArea(10,40);  
    JScrollPane centerPane = new JScrollPane(centerArea);  
    jFrame.add(centerPane);  
    //下  
    JPanel bottomPanel = new JPanel();  
    JTextField bottomTextField = new JTextField(35);  
    JButton sendButton = new JButton("发送");  
    bottomPanel.add(bottomTextField);  
    bottomPanel.add(sendButton);  
    jFrame.add(bottomPanel, BorderLayout.SOUTH);

    jFrame.setLocation(400,300);  
    jFrame.setDefaultCloseOperation(WindowConstants.EXIT\_ON\_CLOSE);  
    jFrame.pack();  
    jFrame.setVisible(true);  
    AtomicReference<String> username = new AtomicReference<>("");  
    //确认用户名事件监听器  
    Action confirmListener = new AbstractAction() {  
        @Override  
        public void actionPerformed(ActionEvent actionEvent) {  
            String topTextContent = topTextField.getText();  
            if (topTextContent != null && ! "".equals(topTextContent)){  
                username.set(topTextContent);  
                System.out.println("用户名设置成功");  
            }  
        }  
    };  
    //绑定事件监听器  
    topButton.addActionListener(confirmListener);  
    topTextField.getInputMap().put(KeyStroke.getKeyStroke('\\n'),"confirm\_username");  
    topTextField.getActionMap().put("confirm\_username",confirmListener);  
    /\*\*  
     \* 发送&接收用socket  
     \*/  
    try{  
        MulticastSocket mClinetSocket = new MulticastSocket(BROAD\_PORT);  
        InetAddress multi\_addr = InetAddress.getByName(BROAD\_IP);  
        /\*\*  
         \* 接收端  
         \*/  
        new Thread(){  
            @Override  
            public void run() {  
                byte\[\] inBuff = new byte\[4096\];  
                /\*\*  
                 \* 构建接收包  
                 \*/  
                DatagramPacket inPacket = new DatagramPacket(inBuff,0,inBuff.length);  
                while (true){  
                    /\*\*  
                     \* 接收数据放包里,转为字符串打印出来  
                     \*/  
                    try {  
                        mClinetSocket.receive(inPacket);  
                    } catch (IOException e) {  
                        e.printStackTrace();  
                    }  
                    String content = new String(inPacket.getData(),0, inPacket.getLength());  
                    System.out.println(content);  
                    centerArea.append(content + "\\n"); //向文本框加入文本  
                }  
            }  
        }.start();  
        /\*\*  
         \* 套接字加入广播地址  
         \*/  
        mClinetSocket.joinGroup(multi\_addr);

        /\*\*  
         \* 服务端地址构建  
         \*/  
        InetAddress server\_addr = InetAddress.getByName(SERVER\_IP);  
        /\*\*  
         \* 构建发出包  
         \*/  
        DatagramPacket outPacket = new DatagramPacket(new byte\[0\],0,server\_addr,SERVER\_PORT);  
        /\*\*  
         \* 发送按钮监听器  
         \*/  
        Action sendListener = new AbstractAction() {  
            @Override  
            public void actionPerformed(ActionEvent actionEvent) {  
                if (username.get().equals("")){  
                    System.err.println("请输入用户名");  
                }else {  
                    String sendContent = bottomTextField.getText();  
                    if (sendContent != null && ! "".equals(sendContent)){  
                        sendContent = username + ":" + sendContent;  
                        //输入字节存入包,  
                        outPacket.setData(sendContent.getBytes());  
                        try {  
                            mClinetSocket.send(outPacket);  
                            bottomTextField.setText(null);  
                        } catch (IOException ex) {  
                            ex.printStackTrace();  
                        }  
                    }  
                }  
            }  
        };  
        /\*\*  
         \* 发送按钮绑定监听器  
         \*/  
        sendButton.addActionListener(sendListener);  
        //内容框监听键盘  
        bottomTextField.getInputMap().put(KeyStroke.getKeyStroke('\\n'),"send");  
        bottomTextField.getActionMap().put("send",sendListener);  
    } catch (IOException e) {  
        e.printStackTrace();  
    }

}

public static void main(String\[\] args) {  
    new MulticastClientSocket().go();  
}  

}