Java入门网络编程-使用UDP通信

程序说明:

以下代码,利用java的网络编程,使用UDP通信作为通信协议,描述了一个简易的多人聊天程序,此程序可以使用公网或者是局域网进行聊天,要求有一台服务器。程序一共分为2个包,第一个包:udp,放置服务器端代码,包括:Server.java,第二个包:ui,放置客户端代码,包括:Login.java,Chat.java,Sender.java,Reciever.java,Test.java,期中Chat与Login为ui界面。

没有公网服务器的同学可以选择阿里云租赁【可以选择云翼计划】【非广告】,或者使用局域网,此代码使用公网ip测试成功,没有试过局域网,感兴趣的同学可以试一试。

具体代码:

Server.java:

 1 package udp;
 2 
 3 import java.io.IOException;
 4 import java.io.UnsupportedEncodingException;
 5 import java.net.*;
 6 import java.util.LinkedList;
 7 import java.util.List;
 8 
 9 /**
10  * @author lanternfirefly
11  * @date 2019/6/10 - 22:18
12  */
13 public class Server
14 {
15 
16     public static void main(String[] args)
17     {
18         new Thread(new Server_Run()).start();
19     }
20 
21 }
22 
23 class Server_Run implements Runnable
24 {
25     DatagramSocket server;
26     DatagramPacket packetIn = null;
27     DatagramPacket packetOut = null;
28     List<InetSocketAddress> addressList = new LinkedList<InetSocketAddress>();
29 
30     public void doForThis() throws IOException
31     {
32         try
33         {
34             InetAddress local = InetAddress.getLocalHost();// 得到本地地址
35             System.out.println("Server local address:" + local);
36             server = new DatagramSocket(8823, local);// 8823负责接收
37 
38             while (true)
39             {
40                 byte[] buff = new byte[4096];
41                 packetIn = new DatagramPacket(buff, 1024);
42                 // 一旦调用这一方法, 会程序的阻塞, 直到你收到有数据报为止。
43                 server.receive(packetIn);
44 
45                 // 每次建立连接,获取用户地址,并存储在列表中
46                 InetSocketAddress clientAddress = (InetSocketAddress) packetIn.getSocketAddress(); // 获取客户端地址
47                 String ip = clientAddress.getAddress().getHostAddress();
48                 int clientport = clientAddress.getPort();// 必须要通过端口号来找到客户端
49                 if (!addressList.contains(clientAddress))
50                 {
51                     addressList.add(new InetSocketAddress(ip, clientport));
52                 }
53 
54                 byte[] temp = packetIn.getData();
55                 int size = packetIn.getLength();
56                 String content = new String(temp, 0, size, "UTF-8");
57                 URLDecoder.decode(content, "utf-8");
58                 if (size > 0)
59                 {
60                     System.out.println(content);
61                 }
62                 URLEncoder.encode(content, "utf-8");
63                 for (InetSocketAddress clientisa : addressList)
64                 {
65                     packetOut = new DatagramPacket(content.getBytes("UTF-8"), 0, content.getBytes("UTF-8").length,
66                             clientisa);// offset=0 偏移量
67                     server.send(packetOut);
68                 }
69 
70             }
71         } catch (SocketException e)
72         {
73             e.printStackTrace();
74         } catch (UnsupportedEncodingException e)
75         {
76             e.printStackTrace();
77         }
78     }
79 
80     public void run()
81     {
82         try
83         {
84             doForThis();
85         } catch (IOException e)
86         {
87             e.printStackTrace();
88         }
89     }
90 
91 }
View Code

Login.java:

  1 package ui;
  2 
  3 import java.awt.FlowLayout;
  4 import java.awt.Font;
  5 import java.awt.Image;
  6 import java.awt.event.ActionEvent;
  7 import java.awt.event.ActionListener;
  8 import java.awt.event.KeyEvent;
  9 import java.awt.event.KeyListener;
 10 import java.io.IOException;
 11 import java.net.ConnectException;
 12 import java.net.InetAddress;
 13 import java.net.UnknownHostException;
 14 import java.util.regex.Pattern;
 15 
 16 import javax.swing.ImageIcon;
 17 import javax.swing.JButton;
 18 import javax.swing.JFrame;
 19 import javax.swing.JLabel;
 20 import javax.swing.JOptionPane;
 21 import javax.swing.JPanel;
 22 import javax.swing.JTextField;
 23 import javax.swing.UIManager;
 24 
 25 class Login implements ActionListener, KeyListener
 26 {
 27 
 28     JFrame frame;
 29     JLabel logo;
 30     JLabel lbl1, lbl2;
 31     JTextField jtf1, jtf2;
 32     JButton jb1, jb2;
 33     JPanel jp1, jp2, jp3, jp4;
 34     ImageIcon img, head;
 35     int width = 400;
 36     int height = 650;
 37 
 38     String address;
 39     String name;
 40     public static void setUIFont()
 41     {
 42         Font f = new Font("宋体",Font.BOLD,18);
 43         String   names[]={ "Label", "CheckBox", "PopupMenu","MenuItem", "CheckBoxMenuItem",
 44                 "JRadioButtonMenuItem","ComboBox", "Button", "Tree", "ScrollPane",
 45                 "TabbedPane", "EditorPane", "TitledBorder", "Menu", "TextArea",
 46                 "OptionPane", "MenuBar", "ToolBar", "ToggleButton", "ToolTip",
 47                 "ProgressBar", "TableHeader", "Panel", "List", "ColorChooser",
 48                 "PasswordField","TextField", "Table", "Label", "Viewport",
 49                 "RadioButtonMenuItem","RadioButton", "DesktopPane", "InternalFrame"
 50         }; 
 51         for (String item : names) {
 52              UIManager.put(item+ ".font",f); 
 53         }
 54     }
 55 
 56     public Login() throws IOException
 57     {
 58         setUIFont();
 59         img = new ImageIcon(Login.class.getResource("/img/logo.jpg"));
 60         head = new ImageIcon(Login.class.getResource("/img/head.png"));// 此句暂时无效
 61         img.setImage(img.getImage().getScaledInstance(width, 360, Image.SCALE_DEFAULT));
 62         lbl1 = new JLabel("请输入服务器地址:");
 63         lbl2 = new JLabel("请输入用户名:");
 64         logo = new JLabel(img);// 预备图片
 65         jb1 = new JButton("加入");
 66         jb2 = new JButton("退出");
 67         jb1.addActionListener(this);
 68         jb2.addActionListener(this);
 69         jtf1 = new JTextField(20);
 70         jtf2 = new JTextField(20);
 71         jp1 = new JPanel();
 72         jp2 = new JPanel();
 73         jp3 = new JPanel();
 74         jp4 = new JPanel();
 75         jp1.add(logo);
 76         jp2.add(lbl1);
 77         jp2.add(jtf1);
 78         jp3.add(lbl2);
 79         jp3.add(jtf2);
 80         jp4.add(jb1);
 81         jp4.add(jb2);
 82 
 83         jtf1.addKeyListener(this);// 绑定enter
 84         jtf2.addKeyListener(this);// 绑定enter
 85         frame = new JFrame("PLMM聊天室");
 86         frame.setIconImage(head.getImage());
 87         frame.setLayout(new FlowLayout(1, 20, 30));
 88         frame.add(jp1);
 89         frame.add(jp2);
 90         frame.add(jp3);
 91         frame.add(jp4);
 92         frame.setLocationRelativeTo(null);
 93         frame.setSize(width, height);
 94         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 95         frame.setVisible(true);
 96     }
 97 
 98     // 以下代码判断ip是否可连接,未完成
 99 //    public boolean isHostConnectable(String host, int port)
100 //    {
101 //        Socket socket = new Socket();
102 //        try
103 //        {
104 //            socket.connect(new InetSocketAddress(host, port));
105 //        } catch (ConnectException e)
106 //        {
107 //            e.printStackTrace();
108 //            return false;
109 //        } catch (IOException e)
110 //        {
111 //            e.printStackTrace();
112 //            return false;
113 //        } finally
114 //        {
115 //            try
116 //            {
117 //                socket.close();
118 //            } catch (IOException e)
119 //            {
120 //                e.printStackTrace();
121 //            }
122 //        }
123 //        return true;
124 //    }
125 
126 //    // 以下代码判断ip是否超时
127     public boolean isHostReachable(String host, Integer timeOut)
128     {
129         try
130         {
131             return InetAddress.getByName(host).isReachable(timeOut);
132         } catch (ConnectException e)
133         {
134             e.printStackTrace();
135         } catch (UnknownHostException e)
136         {
137             e.printStackTrace();
138         } catch (IOException e)
139         {
140             e.printStackTrace();
141         }
142         return false;
143     }
144 
145     public void connect()
146     {
147         // 下面判断用户名
148         if (jtf2.getText().length() > 8)
149         { // 对用户名长度进行限制
150             JOptionPane.showMessageDialog(null, "用户名长度必须小于8!", "Warning", JOptionPane.ERROR_MESSAGE);
151         } else if (jtf2.getText().length() == 0)
152         { // 为空判断
153             JOptionPane.showMessageDialog(null, "用户名不能为空!", "Warning", JOptionPane.ERROR_MESSAGE);
154         } else
155         {
156             name = jtf2.getText();
157         }
158         // 下面判断ip地址
159         address = jtf1.getText();
160         Pattern pattern = Pattern.compile(
161                 "^(\\d|[1-9]\\d|1\\d{2}|2[0-5][0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-5][0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-5][0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-5][0-5])$"); // IP正则表达式
162         boolean flag = pattern.matcher(address).matches();
163         if (address.length() == 0)
164         { // 为空判断
165             JOptionPane.showMessageDialog(null, "IP地址不能为空!", "Warning", JOptionPane.ERROR_MESSAGE);
166         } else if (flag == false)
167         { // IP正则匹配
168             JOptionPane.showMessageDialog(null, "IP地址不正确!", "Warning", JOptionPane.ERROR_MESSAGE);
169         } else if (isHostReachable(address, 1000) == false)
170         {// 判断是否连接超时
171             JOptionPane.showMessageDialog(null, "IP连接超时!", "Warning", JOptionPane.ERROR_MESSAGE);
172         } else
173         {
174             frame.setVisible(false); // 如何实现真正的关闭?且不退出程序(不中断下一步执行)
175             frame = null;
176             frame = new Chat(address, name);// 传入address
177         }
178     }
179 
180     public void actionPerformed(ActionEvent e)
181     {
182         if (e.getSource() == jb1)
183         {
184             connect();
185 
186         } else if (e.getSource() == jb2)
187         {
188             System.exit(0);
189         }
190     }
191 
192     public void keyTyped(KeyEvent e)
193     {
194 
195     }
196 
197     public void keyPressed(KeyEvent e)
198     {
199         if (e.getKeyCode() == KeyEvent.VK_ENTER)
200         {
201             connect();
202         }
203 
204     }
205 
206     public void keyReleased(KeyEvent e)
207     {
208 
209     }
210 
211 }
View Code

Chat.java:

  1 package ui;
  2 
  3 import java.awt.BorderLayout;
  4 import java.awt.FlowLayout;
  5 import java.awt.event.ActionEvent;
  6 import java.awt.event.ActionListener;
  7 import java.awt.event.KeyEvent;
  8 import java.awt.event.KeyListener;
  9 
 10 import javax.swing.JButton;
 11 import javax.swing.JFrame;
 12 import javax.swing.JOptionPane;
 13 import javax.swing.JPanel;
 14 import javax.swing.JScrollPane;
 15 import javax.swing.JSplitPane;
 16 import javax.swing.JTextArea;
 17 
 18 public class Chat extends JFrame implements ActionListener, KeyListener
 19 {
 20     private static final long serialVersionUID = 1L;
 21     double initWidth = 800;
 22     double initHeight = 600;
 23 
 24     JPanel jp1, jp2; // 定义面板
 25     JSplitPane jsp; // 定义拆分窗格
 26     JTextArea jta1, jta2; // 定义文本域
 27     JScrollPane jspane1, jspane2; // 定义滚动窗格
 28 
 29     JButton jb1, jb2; // 定义按钮
 30 
 31     String addressSTR;
 32     String name;
 33     String messageOut = "";
 34     String messageIn = "";
 35 
 36     public Chat(String addressSTR, String name)
 37     {
 38 
 39         this.addressSTR = addressSTR;
 40         this.name = name;
 41 
 42         jta1 = new JTextArea(); // 创建多行文本框
 43         jta2 = new JTextArea();
 44         jta1.setLineWrap(true); // 设置多行文本框自动换行
 45         jta1.setEditable(false); // 禁止用户修改公屏信息
 46         jta1.addKeyListener(this);
 47         jta2.setLineWrap(true);
 48         jta2.addKeyListener(this);
 49         jspane1 = new JScrollPane(jta1); // 创建滚动窗格
 50         jspane2 = new JScrollPane(jta2);
 51         jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, jspane1, jspane2); // 创建拆分窗格
 52 
 53         double initSep = initHeight * 2 / 3;
 54 
 55         jsp.setDividerLocation((int) initSep); // 设置拆分窗格分频器初始位置
 56         jsp.setDividerSize(1); // 设置分频器大小
 57         jb1 = new JButton("发送"); // 创建按钮
 58         jb2 = new JButton("关闭");
 59         jb1.addActionListener(this);
 60         jb2.addActionListener(this);
 61         jp1 = new JPanel(); // 创建面板
 62         jp2 = new JPanel();
 63         jp1.setLayout(new BorderLayout()); // 设置面板布局
 64         jp2.setLayout(new FlowLayout(FlowLayout.RIGHT));
 65         jp1.add(jsp); // 分频器
 66         jp2.add(jb1); // 按钮
 67         jp2.add(jb2);
 68 
 69         this.add(jp1, BorderLayout.CENTER);
 70         this.add(jp2, BorderLayout.SOUTH);
 71 
 72         // 设置窗体实行
 73         this.setTitle("澳门聊天室-人间天堂"); // 设置界面标题
 74 //            this.setIconImage(new ImageIcon(User_chat.class.getClassLoader().getResource("Head.png")).getImage());
 75         this.setSize((int) initWidth, (int) initHeight); // 设置界面像素
 76         this.setLocationRelativeTo(null); // 居中运行
 77         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置虚拟机和界面一同关闭
 78         this.setVisible(true); // 设置界面可视化
 79 
 80         new Thread(new Reciever(addressSTR, messageIn, jta1)).start();
 81     }
 82 
 83     public void sendOut()
 84     {
 85         messageOut = jta2.getText();
 86         if (messageOut.hashCode() != 0) // 此处必须用hash
 87         {
 88             new Thread(new Sender(addressSTR, name, messageOut)).start();
 89             jta2.setText("");
 90         } else
 91         {
 92             JOptionPane.showMessageDialog(null, "发送消息不能为空!", "Warning", JOptionPane.ERROR_MESSAGE);
 93         }
 94 
 95     }
 96 
 97     public void scrollAndSetCursor(JTextArea jta)
 98     {
 99         // 自动滚动
100         jta.setSelectionStart(jta.getText().length());
101 
102     }
103 
104     public void actionPerformed(ActionEvent e)
105     {
106         if (e.getSource() == jb1)
107         {
108             // 发送
109             sendOut();
110 
111         } else if (e.getSource() == jb2)
112         {
113             System.exit(0);
114 
115         }
116     }
117 
118     public void keyTyped(KeyEvent e)
119     {
120 
121     }
122 
123     public void keyPressed(KeyEvent e)
124     {
125         if (e.getKeyCode() == KeyEvent.VK_CONTROL + KeyEvent.VK_ENTER)
126         {
127             sendOut();
128         }
129     }
130 
131     public void keyReleased(KeyEvent e)
132     {
133         if (e.getKeyCode() == KeyEvent.VK_ENTER)
134         {
135             jta2.setText("");
136         }
137 
138     }
139 }
View Code

Sender.java:

 1 package ui;
 2 
 3 import java.io.IOException;
 4 import java.net.DatagramPacket;
 5 import java.net.DatagramSocket;
 6 import java.net.InetSocketAddress;
 7 import java.net.SocketException;
 8 import java.net.URLEncoder;
 9 import java.net.UnknownHostException;
10 
11 public class Sender implements Runnable
12 {
13     String address = "";
14     String name = "";
15     DatagramSocket socket = null;
16     DatagramPacket packetOut = null;
17     String messageOut = "";
18 
19     public Sender(String address, String name, String messageOut)
20     {
21         this.address = address;
22         this.name = name;
23         this.messageOut = messageOut;
24     }
25 
26     public void doForThis() throws IOException
27     {
28 
29         try
30         {
31             if (messageOut.length() != 0)
32             {
33                 socket = new DatagramSocket(8823); // 进行一次发送
34                 InetSocketAddress isa = new InetSocketAddress(address, 8823);
35 
36                 String content = name + ":" + messageOut;
37                 URLEncoder.encode(content, "utf-8");
38 
39                 packetOut = new DatagramPacket(content.getBytes("UTF-8"), content.getBytes("UTF-8").length, isa);
40                 socket.send(packetOut);
41                 messageOut = "";
42                 socket.close();
43 //                    socket.receive(packetOut);
44 //                    String message = new String(packetOut.getData(), 0, packetOut.getLength());
45 //                    System.out.println("本机端口和IP信息:" + message);
46 //                    int clientListenPort = Integer.valueOf(message.split(":")[1]);
47 //                    System.out.println(clientListenPort);
48             }
49         } catch (SocketException e)
50         {
51             e.printStackTrace();
52         } catch (UnknownHostException e)
53         {
54             e.printStackTrace();
55         } finally
56         {
57 
58         }
59 
60     }
61 
62     public void run()
63     {
64         try
65         {
66             System.out.println("sender is running");
67             doForThis();
68         } catch (IOException e)
69         {
70             e.printStackTrace();
71 
72         }
73     }
74 
75 }
View Code

Reciever.java:

 1 package ui;
 2 
 3 import java.io.IOException;
 4 import java.io.UnsupportedEncodingException;
 5 import java.net.DatagramPacket;
 6 import java.net.DatagramSocket;
 7 import java.net.InetSocketAddress;
 8 import java.net.SocketAddress;
 9 import java.net.SocketException;
10 import java.net.URLDecoder;
11 
12 import javax.swing.JTextArea;
13 
14 public class Reciever implements Runnable
15 {
16     String strAddress = "";
17     String name = "";
18     String messageOut = "";
19     String messageIn = "";
20     DatagramSocket socket = null;
21     DatagramPacket packet = null;
22     int clientListenPort;
23     JTextArea jta1;
24 
25     public Reciever(String strAddress, String messageIn, JTextArea jta1)
26     {
27         this.strAddress = strAddress;
28         this.messageIn = messageIn;
29         this.jta1 = jta1;
30     }
31 
32 //    @SuppressWarnings("resource") 此处也不能加
33     public void doForThis() throws IOException
34     {
35         SocketAddress server = new InetSocketAddress(strAddress, 8823);
36         @SuppressWarnings("resource")
37         DatagramSocket ds = new DatagramSocket(); // ds必须经过一次发送和接收,为什么呢?
38         byte buff[] = new byte[1024];
39         DatagramPacket dp = new DatagramPacket(buff, 0, 0, server);
40         ds.send(dp);// 发送信息到服务器
41         ds.receive(dp);
42         try
43         {
44             socket = new DatagramSocket(); // 负责接收
45 
46             while (true)
47             {
48                 packet = new DatagramPacket(buff, 1024);// 实现接收
49 
50                 ds.receive(packet);
51 
52                 byte[] temp = packet.getData();
53                 int size = packet.getLength();
54                 if (size > 0)
55                 {
56                     String content = new String(temp, 0, size, "UTF-8");
57 //                        System.out.println(content);
58                     URLDecoder.decode(content, "utf-8");
59                     messageIn = "\n" + content;
60                     jta1.append(messageIn);
61                     // 自动滚动
62                     scrollAndSetCursor(jta1);
63                 }
64             }
65         } catch (SocketException e)
66         {
67             e.printStackTrace();
68         } catch (UnsupportedEncodingException e)
69         {
70             e.printStackTrace();
71         }
72     }
73 
74     public void scrollAndSetCursor(JTextArea jta)
75     {
76         // 自动滚动
77         jta.setSelectionStart(jta.getText().length());
78 
79     }
80 
81     public void run()
82     {
83         try
84         {
85             System.out.println("receiver is running");
86             doForThis();
87         } catch (IOException e)
88         {
89             e.printStackTrace();
90         }
91     }
92 }
View Code

Test.java:

 1 package ui;
 2 
 3 import java.io.IOException;
 4 
 5 public class Test
 6 {
 7     public static void main(String[] args) throws IOException
 8     {
 9         new Login();
10     }
11 }
View Code

测试结果:

注意我Login代码里的img,没有在对应路径放置图片的话是无法显示的,我放在bin目录下自己创建的img文件夹里。

登录界面,【忽略窗口名】:

登录进去后,测试聊天【忽略窗口名】:

转载请说明原文地址,谢谢!

猜你喜欢

转载自www.cnblogs.com/linkchen/p/11106813.html