java课编程作业:在老师给的demo的基础上实现客户端发送数据到服务器端,服务器端接受客户端后进行数据广播。
整体功能类似于聊天室,代码部分不是太难,但是在本机测试的时候出现这样的问题:
服务端通过将每一个Socket客户端的IP存入Set集合,每次接受到数据后都向当前所有的IP转发。但是本机演示的时候所有开的ChatClient客户端都是同一IP,怎么测试呢?
解决办法就是本机测试时候服务端向多个不同的端口转发就好了,这样跑起来的客户端是在不同端口上进行监听的(只是为了实现广播,实际应用下还是通过IP来转发)。
客户端代码:
import javax.print.attribute.HashPrintServiceAttributeSet; import javax.swing.*; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Toolkit; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.*; public class ChatClient extends JFrame { JTextArea contents = new JTextArea(); JLabel label1 = new JLabel("服务器地址"); JTextField address = new JTextField(); JLabel label2 = new JLabel("用户名"); JTextField username = new JTextField(); JButton online = new JButton("上线"); JButton offline = new JButton("下线"); JTextField input = new JTextField(); JButton send = new JButton("发送"); boolean onlineFlag = false; public ChatClient() { super("Chat Client"); contents.setEditable(false); getContentPane().add(contents); JPanel p1 = new JPanel(); p1.setLayout(new FlowLayout(FlowLayout.LEADING)); address.setPreferredSize(new Dimension(100, 28)); username.setPreferredSize(new Dimension(80, 28)); input.setPreferredSize(new Dimension(180, 28)); p1.add(label1); p1.add(address); p1.add(label2); p1.add(username); p1.add(online); p1.add(offline); p1.add(input); p1.add(send); offline.setEnabled(false); send.setEnabled(false); getContentPane().add(p1, BorderLayout.SOUTH); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { onlineFlag = false; Map<String, Object> m1 = new HashMap<String, Object>(); m1.put("time", new Date()); // 上线时间 m1.put("username", getUsername()); m1.put("onlineFlag", onlineFlag); send(getServerAddress(), 2222, m1); System.exit(0); } }); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent ae) { String server = getServerAddress(); if (server == null || server.length() == 0) { JOptionPane.showMessageDialog(ChatClient.this, "请输入服务器地址!"); return; } if (ae.getSource() == online) { // 用户点击了上线按钮 if (!onlineFlag) { onlineFlag = true; Map<String, Object> m1 = new HashMap<String, Object>(); m1.put("time", new Date()); // 上线时间 m1.put("username", getUsername()); m1.put("onlineFlag", onlineFlag); online.setEnabled(false); offline.setEnabled(true); send.setEnabled(true); address.setEnabled(false); username.setEnabled(false); send(getServerAddress(), 2222, m1); } } else { // 用户点击了下线按钮 if (onlineFlag) { onlineFlag = false; Map<String, Object> m1 = new HashMap<String, Object>(); m1.put("time", new Date()); // 上线时间 m1.put("username", getUsername()); m1.put("onlineFlag", onlineFlag); online.setEnabled(true); offline.setEnabled(false); send.setEnabled(false); address.setEnabled(true); username.setEnabled(true); send(getServerAddress(), 2222, m1); } } } }; ActionListener al2 = new ActionListener() { public void actionPerformed(ActionEvent ae) { Map<String, Object> m1 = new HashMap<String, Object>(); m1.put("time", new Date()); m1.put("username", getUsername()); m1.put("input", getInput()); send(getServerAddress(), 2222, m1); } }; send.addActionListener(al2); online.addActionListener(al); offline.addActionListener(al); // setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(720, 400); Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((d.width - 720) / 2, (d.height - 400) / 2); setVisible(true); new Thread() { public void run() { try { // 在2221端口接收服务器发送过来的聊天内容,包括上线,下线消息。 DatagramSocket rSocket = new DatagramSocket(2220); while (true) { byte[] buffer = new byte[1024 * 16]; DatagramPacket recvPacket = new DatagramPacket(buffer, buffer.length); rSocket.receive(recvPacket); byte[] data = recvPacket.getData(); byte[] recvData = new byte[recvPacket.getLength()]; System.arraycopy(buffer, 0, recvData, 0, recvData.length); //将从服务器接收的数据存入Map并显示 Map<String, Object> map = convertByteArrayToMap(recvData); if (map.containsKey("onlineFlag")) { // 上线或者下线 boolean b = (boolean) map.get("onlineFlag"); if (b) { // 上线 map.put("input", "上线"); } else { // 下线 map.put("input", "下线"); } } Date time = (Date) map.get("time"); String s = convertDateToFormatString(time); String user = (String) map.get("username"); user = "[" + user + "]"; contents.append(s + user + ": " + (String) map.get("input") + "\r\n"); } } catch (Exception e) { e.printStackTrace(); } } }.start(); } public String getServerAddress() { String s = address.getText(); //trim删除多余空格 s = s.trim(); return s; } public String getUsername() { String s = username.getText(); s = s.trim(); if (s == null || s.length() == 0) { s = "匿名"; } return s; } public String getInput() { String s = input.getText(); s = s.trim(); if (s == null || s.length() == 0) { s = "不想说什么"; } return s; } public static String convertDateToFormatString(Date d) { return new java.text.SimpleDateFormat("(yyyy-MM-dd HH:mm:ss)").format(d); } public static void send(String ip, int port, Map<String, Object> map) { try { byte[] data = convertMapToByteArray(map); DatagramSocket socket = new DatagramSocket(); DatagramPacket packet = new DatagramPacket(data, data.length); packet.setSocketAddress(new InetSocketAddress(ip, port)); socket.send(packet); } catch (Exception e) { e.printStackTrace(); } } public static byte[] convertMapToByteArray(Map<String, Object> map) { try { //完成此代码块,要求将map集合中的数据转换成字节数组 ByteArrayOutputStream b = new ByteArrayOutputStream(); ObjectOutputStream ois = new ObjectOutputStream(b); ois.writeObject(map); byte[] temp = b.toByteArray(); return temp; } catch (Exception e) { e.printStackTrace(); } return null; } public static Map<String, Object> convertByteArrayToMap(byte[] data) { try { ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); Map<String, Object> result = (Map<String, Object>) ois.readObject(); return result; } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { new ChatClient(); } }
服务端代码:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; public class ChatServer { LinkedBlockingQueue<Map<String, Object>> queue = new LinkedBlockingQueue<Map<String, Object>>(); Set<String> onlineIPs = new HashSet<String>(); // 当前在线IP。 public ChatServer() { try { new Thread() { public void run() { while (true) { try { //补齐程序,要求从接收队列中取出数据,遍历在线IP集合,向所有在线IP发送数据 if( !queue.isEmpty() ){ if( !onlineIPs.isEmpty() ){ for(String ip : onlineIPs){ //下面的端口号可以自定义增加,初始转发至2220端口 多增加的端口可以实现本机内的广播 ChatClient.send(ip, 2221, queue.peek() ); ChatClient.send(ip, 2220, queue.remove() ); } } } //end } catch (Exception e) { e.printStackTrace(); } } } }.start(); //接受端口2222 DatagramSocket serverSocket = new DatagramSocket(2222); System.out.println("Chat Server started."); while (true) { byte[] buffer = new byte[1024 * 16]; DatagramPacket recvPacket = new DatagramPacket(buffer, buffer.length); serverSocket.receive(recvPacket); InetSocketAddress remoteAddress = new InetSocketAddress(recvPacket.getAddress(), recvPacket.getPort()); System.out.println("接收到 " + remoteAddress.getHostString() + " 发送过来的数据包"); byte[] data = recvPacket.getData(); byte[] recvData = new byte[recvPacket.getLength()]; System.arraycopy(buffer, 0, recvData, 0, recvData.length); System.out.println(recvData.length); Map<String, Object> map = ChatClient.convertByteArrayToMap(recvData); System.out.print("map长度"+map.size()); if (map.containsKey("onlineFlag")) { // 上线或者下线 boolean b = (boolean) map.get("onlineFlag"); if (b) { // 上线 onlineIPs.add(remoteAddress.getHostString()); } else { // 下线 onlineIPs.remove(remoteAddress.getHostString()); } } queue.add(map); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { new ChatServer(); } }