java简单群聊功能
使用了Socket,swing,线程。
原本是只准备写两人间的通信,是后来才想改成这样的,所以代码有点乱。
服务器类(Server)
有个set集合,当服务器群发消息时就要遍历它。
public class Server_ZZ {
public static void main(String[] args) throws IOException {
ServerSocket ss87 = new ServerSocket(10087); //用来存储连接的Socket HashSet<Socket> li=new HashSet<>(); //服务器界面 Swing_Server sw=new Swing_Server(); //不停的接受Socket,将其放入一个集合里面 while(true){
Socket s87 = ss87.accept(); li.add(s87); sw.addSocket(s87, li); } } }
讯享网
服务器界面(Swing_Server)
发送消息的功能是在这个类中完成的,通过触发button的点击事件,来发送消息。用线程来接收消息
发送消息的流程为:
通过Socket得到OutputStream os;
os.write()//写入你要发送的消息,要转换成byte[]型;
接受消息:
因为消息是一直要接收的,如果不用线程则进行不了其他操作。RecepteThread类
讯享网public class Swing_Server extends JFrame {
HashSet<Socket> li; Socket s; OutputStream os; String ip;//自己的ip地址 String cip;//客户端的IP地址 JTextArea ja1; public void addSocket(Socket soc,HashSet<Socket> li) throws IOException{
s=soc;//当前集合通信 this.li=li;//socket通信集合 os= s.getOutputStream();//socket中的输出流 ip=InetAddress.getLocalHost().toString();//拿到自己的ip地址 cip=s.getInetAddress().getHostAddress();//拿到对方的ip地址 System.out.println(ip+"自己的ip地址"); System.out.println(cip+"客户端的ip地址"); setTitle("本机主机名/ip地址:"+ip);// 窗体标题 // 当读到消息就把消息放入文本域,如果不用线程就会在这里一直执行 RecepteThread rt = new ServerRecepteThread(s, this.ja1,this.li); new Thread(rt).start(); } public Swing_Server() throws IOException {
setDefaultCloseOperation(EXIT_ON_CLOSE);// 关闭窗口时关闭程序 //setTitle("本机主机名/ip地址:"+ip);// 窗体标题 setResizable(false);// 禁止改变窗体大小 setBounds(500, 100, 0, 0);// 窗体坐标 setSize(700, 550);// 窗体大小 setVisible(true);// 窗体可见 setBackground(Color.white); Container c = getContentPane();// 拿到窗体的容器 c.setLayout(new FlowLayout(FlowLayout.LEFT)); // 消息文本域 ja1 = new JTextArea(); ja1.setEditable(false);// 不可编辑 ja1.setSize(300, 500);// 文本框大小 ja1.setFont(new Font("楷体", Font.PLAIN, 18));// 字体 ja1.setLineWrap(true);// 自动换行 ja1.setBackground(Color.WHITE); ja1.setRows(17); ja1.setColumns(67); // 发送消息文本域 JTextArea ja2 = new JTextArea(); ja2.setSize(100, 500);// 文本框大小 ja2.setFont(new Font("楷体", Font.PLAIN, 16)); ja2.setRows(7);// 行数 ja2.setColumns(75);// 列数 // 发送按钮 JButton jb = new JButton(); jb.setText("发送"); //监听事件 当按下发送键时 发送消息 jb.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
msg(ja1,ip,ja2.getText());// 在自己的屏幕上显示自己发的消息 //写入流 try {
// 服务器自己发送的消息全部发一遍 for (Socket so : li) {
os = so.getOutputStream(); //将ip地址和消息内容用“&”隔开,这意味发送的消息里不能& String ipmsg=ip+"&"+ja2.getText(); os.write(ipmsg.getBytes()); } } catch (IOException e1) {
e1.printStackTrace(); } ja2.setText("");// 清空文本 } }); // 将组件加入窗体 c.add(ja1); c.add(ja2); c.add(jb); // 刷新 c.validate(); } // 在自己的屏幕上显示自己发的消息 public static void msg(JTextArea j,String ip,String msg) {
String str = "主机名/ip地址:" +ip+ "\n" + msg + "\n"; j.append(str); } }
服务器接收消息线程类(ServerRecepteThread)
这个类是继承的RecepteThread,RecepteThread就是我的客户端接收消息线程类,我重写了run方法。我这种写法是不对的,我开头也讲了,临时改的,就偷了点懒,这里应该用接口可能会更好点。
public class ServerRecepteThread extends RecepteThread implements Runnable {
OutputStream os; HashSet<Socket> li; ServerRecepteThread(Socket s, JTextArea ja1, HashSet<Socket> li) throws IOException {
super(s, ja1); this.li = li; this.os = s.getOutputStream(); } @Override public void run() {
try {
int len; byte[] bys = new byte[1024]; bi = s.getInputStream(); String read; while (true) {
// 接受消息 len = bi.read(bys); read = new String(bys, 0, len); // 写入消息文本域ja1 ja1.append("ip地址:" + cip + "\n" + read + "\n"); // 服务器接收到客户端消息,就除该客户端都发送一遍 for (Socket so : li) {
if (!so.equals(s)) {
os = so.getOutputStream(); String reads = new String(cip + "&" + read); os.write(reads.getBytes()); } } // 如果接受到“gg” 就结束 if (read.equals("gg")) {
s.close(); break; } } } catch (IOException e) {
e.printStackTrace(); } } }
(客户端)接收消息线程类(RecepteThread)
讯享网public class RecepteThread implements Runnable {
Socket s; JTextArea ja1; String cip; InputStream bi; socket 显示文本框 RecepteThread(Socket s, JTextArea ja1) {
this.s = s; this.ja1 = ja1; // 对方的ip地址 this.cip = s.getInetAddress().getHostAddress(); } @Override public void run() {
try {
int len; byte[] bys = new byte[1024]; bi = s.getInputStream(); String read; while (true) {
// 接受消息 len = bi.read(bys); read = new String(bys, 0, len); // 用&分割 String[] reads = read.split("&"); // 写入消息文本域ja1 ja1.append("ip地址:" + reads[0] + "\n" + reads[1] + "\n"); // 如果接受到“gg” 就结束 if (read.equals("gg")) {
s.close(); break; } } } catch (IOException e) {
e.printStackTrace(); } } }
客户端类(Client)
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket s87 = new Socket(InetAddress.getByName(/*服务器的IP地址 字符串*/), 10087); System.out.println("Link start"); new Swing_Client(s87); } }
客户端界面(Swing_Client)
这个类其实应该和服务端界面类,抽象后继承同一个父类,因为这两者有很多相同的代码,仅接收消息和发送消息的操作不同,界面设计是一样的。
讯享网public class Swing_Client extends JFrame {
public Swing_Client(Socket soc) throws IOException {
Socket s=soc;//当前集合通信 OutputStream os= s.getOutputStream();//socket中的输出流 String ip=InetAddress.getLocalHost().toString();//拿到自己的ip地址 String cip=s.getInetAddress().getHostAddress();//拿到对方的ip地址 System.out.println(ip+"自己的ip地址"); System.out.println(cip+"对方的ip地址"); setDefaultCloseOperation(EXIT_ON_CLOSE);// 关闭窗口时关闭程序 setTitle("本机主机名/ip地址:"+ip);// 窗体标题 setResizable(false);// 禁止改变窗体大小 setBounds(500, 100, 0, 0);// 窗体坐标 setSize(700, 550);// 窗体大小 setVisible(true);// 窗体可见 setBackground(Color.white); Container c = getContentPane();// 拿到窗体的容器 c.setLayout(new FlowLayout(FlowLayout.LEFT)); // 消息文本域 JTextArea ja1 = new JTextArea(); ja1.setEditable(false);// 不可编辑 ja1.setSize(300, 500);// 文本框大小 ja1.setFont(new Font("楷体", Font.PLAIN, 18));// 字体 ja1.setLineWrap(true);// 自动换行 ja1.setBackground(Color.WHITE); ja1.setRows(17); ja1.setColumns(67); //当读到消息就把消息放入文本域,如果不用线程就会在这里一直执行 RecepteThread rt=new RecepteThread(s,ja1); new Thread(rt).start(); // 发送消息文本域 JTextArea ja2 = new JTextArea(); ja2.setSize(100, 500);// 文本框大小 ja2.setFont(new Font("楷体", Font.PLAIN, 16)); ja2.setRows(7);// 行数 ja2.setColumns(75);// 列数 // 发送按钮 JButton jb = new JButton(); jb.setText("发送"); //监听事件 当按下发送键时 write入流 jb.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
msg(ja1,ip,ja2.getText());// 发送消息 //写入流 try {
os.write(ja2.getText().getBytes()); } catch (IOException e1) {
e1.printStackTrace(); } ja2.setText("");// 清空文本 } }); // 将组件加入窗体 c.add(ja1); c.add(ja2); c.add(jb); // 刷新 c.validate(); } public static void msg(JTextArea j,String ip,String msg) {
String str = "主机名/ip地址:" +ip+ "\n" + msg + "\n"; j.append(str); } }
效果图:

带有主机名就是自己发的消息。服务器和客户端页面一样。要先开服务器,再开客户端。
总结:
代码有很多需要优化的地方,比如我上面提到的代码的复用,不能发送&,以及关闭窗口不能同时关闭线程问题,所以通过关闭窗口来下线再上线会报错。还有可以增加的功能:比如自己发的消息右对齐,别人的左对齐;右边空出来的可以做成 上线人列表,可以发起单独聊天等。下次在改! 下次!下次!咕咕咕

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/120182.html