程序说明:
以下代码,利用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 public class Server
10 {
11
12 public static void main(String[] args)
13 {
14 new Thread(new Server_Run()).start();
15 }
16
17 }
18
19 class Server_Run implements Runnable
20 {
21 DatagramSocket server;
22 DatagramPacket packetIn = null;
23 DatagramPacket packetOut = null;
24 List<InetSocketAddress> addressList = new LinkedList<InetSocketAddress>();
25
26 public void doForThis() throws IOException
27 {
28 try
29 {
30 InetAddress local = InetAddress.getLocalHost();// 得到本地地址
31 System.out.println("Server local address:" + local);
32 server = new DatagramSocket(8823, local);// 8823负责接收
33
34 while (true)
35 {
36 byte[] buff = new byte[4096];
37 packetIn = new DatagramPacket(buff, 1024);
38 // 一旦调用这一方法, 会程序的阻塞, 直到你收到有数据报为止。
39 server.receive(packetIn);
40
41 // 每次建立连接,获取用户地址,并存储在列表中
42 InetSocketAddress clientAddress = (InetSocketAddress) packetIn.getSocketAddress(); // 获取客户端地址
43 String ip = clientAddress.getAddress().getHostAddress();
44 int clientport = clientAddress.getPort();// 必须要通过端口号来找到客户端
45 if (!addressList.contains(clientAddress))
46 {
47 addressList.add(new InetSocketAddress(ip, clientport));
48 }
49
50 byte[] temp = packetIn.getData();
51 int size = packetIn.getLength();
52 String content = new String(temp, 0, size, "UTF-8");
53 URLDecoder.decode(content, "utf-8");
54 if (size > 0)
55 {
56 System.out.println(content);
57 }
58 URLEncoder.encode(content, "utf-8");
59 for (InetSocketAddress clientisa : addressList)
60 {
61 packetOut = new DatagramPacket(content.getBytes("UTF-8"), 0, content.getBytes("UTF-8").length,
62 clientisa);// offset=0 偏移量
63 server.send(packetOut);
64 }
65
66 }
67 } catch (SocketException e)
68 {
69 e.printStackTrace();
70 } catch (UnsupportedEncodingException e)
71 {
72 e.printStackTrace();
73 }
74 }
75
76 public void run()
77 {
78 try
79 {
80 doForThis();
81 } catch (IOException e)
82 {
83 e.printStackTrace();
84 }
85 }
86
87 }
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 }
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 }
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 }
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 }
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 }
测试结果:
注意我Login代码里的img,没有在对应路径放置图片的话是无法显示的,我放在bin目录下自己创建的img文件夹里。
登录界面,【忽略窗口名】:

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

转载请说明原文地址,谢谢!
来源:https://www.cnblogs.com/linkchen/p/11106813.html
