• java swing+socket实现多人聊天程序


    swing+socket实现多人聊天程序

    1.准备工作

    先看效果:

    • 客户端项目结构图:

    • 服务端项目结构图:

     

    2.运行原理

    • 服务端

    先开一个线程serverListerner,线程中开启一个Serversocket
    用Serversocket.accept()监听指定端口
    一旦有socket连接进来,就为该socket开启一个线程,用于读取该socket输入流的信息,一旦有信息,就通知其他客户端
    并将线程保存到Vector<ChartThread> 集合内,交给ChatThreadManager管理,主要是处理分发消息

    • 客户端:

    点击connect按钮和服务端建立连接,客户端会开启线程随时监听socket的输入流
    服务端accept后就会为该socket建立线程,监控该socket的流信息
    当客户端点击发送按钮时,将文本框内的信息,写进socket中,此时服务端的线程抓到了socket的输入流信息,就让ChatThreadManager管理类,去通知集合内其他socket,为多个socket写入相同的信息,那么其余的客户端就收到消息了,实现了多人聊天的功能

     

    3.核心代码

    • 服务端:

    StartServer.java

    Main函数入口,实例ServerListener类,并开启该线程

    package com.chart;
    
    public class StartServer
    {
       public static void main(String[] args)
       {
    	   new ServerListener().start();
       }
    }

    ServerListener.java:

    用Serversocket.accept()监听指定端口,一旦有socket连接进来,就为该socket开启一个ChatThread线程
    并将ChatThread线程保存到Vector<ChartThread> 集合内,交给ChatThreadManager管理,

    package com.chart;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import javax.swing.JOptionPane;
    
    
    public class ServerListener extends Thread{
       
    	public void run()
    	{
    		try {
    			ServerSocket serversocket= new ServerSocket(23456);
    			while(true) {
    			Socket socket=serversocket.accept();  //进程会阻塞在这句,直到有socket连接进来,就往下执行
    	        System.out.println("one client has connected");
    			ChatThread cs=new ChatThread(socket);
    			cs.start();
    			ChatThreadManager.getChartManager().add(cs);
    			}  
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

    ChatThread:

    在此线程中监听客户端socket的来信 并调用publish()方法,通知ChatThreadManager类

    package com.chart;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    public class ChatThread extends Thread {
    	
    	Socket socket;
    
    	public ChatThread(Socket s) {
    		this.socket = s;
    	}
    
    	public void out(String out) {
    		try {
    			socket.getOutputStream().write(out.getBytes("UTF-8"));
    		} catch (Exception e) {
    		}
    	}
    
    	public void run() {
    		try {
    			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
    			String line = "";
    			while ((line = br.readLine()) != null) {
    				System.out.println(line);
    				ChatThreadManager.getChartManager().publish(this, line);
    			}
    			br.close();
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    	}
    }
    

     ChatThreadManager.java

    单例类,构造函数私有,管理多个ChatThread, 用于通知其他客户端

    package com.chart;
    
    import java.util.Vector;
    
    public class ChatThreadManager {
    
    	private ChatThreadManager() {
    
    	}
    
    	private static final ChatThreadManager Cm = new ChatThreadManager();
    
    	public static ChatThreadManager getChartManager() {
    		return Cm;
    	}
    
    	Vector<ChatThread> ChatSocket_vector = new Vector<ChatThread>();
    
    	public void add(ChatThread cs) {
    		ChatSocket_vector.add(cs);
    	}
    
    	public void publish(ChatThread cs, String msg) {
    		for (int i = 0; i < ChatSocket_vector.size(); i++) {
    			ChatThread csTemp = ChatSocket_vector.get(i);
    			if (!cs.equals(csTemp)) {
    				csTemp.out(msg + "
    ");
    			}
    		}
    	}
    }
    
    • 客户端:

    StartClient.java:

    main 函数入口,新建主窗体,并显示

    package com.client;
    
    import java.awt.EventQueue;
    
    import view.MyClientWindow;
    
    public class StartClient {
    
    	public static void main(String[] args) {
    	  EventQueue.invokeLater(new Runnable() {
    		
    		@Override
    		public void run() {
    			try {
    				MyClientWindow frame=new MyClientWindow();
    				frame.setVisible(true);
    				ConnectionManager.getChatManager().setWindow(frame);
    			}
    			catch (Exception e) {
    			}
    		}
    	});
    	}
    }
    

    MyClientWindow.java:

    继承JFrame,布局窗体,connect按钮执行建立连接事件,send按钮则往socket中写入发送信息

    
    package view;
    
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    
    import javax.swing.GroupLayout;
    import javax.swing.GroupLayout.Alignment;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    import javax.swing.LayoutStyle.ComponentPlacement;
    import javax.swing.border.EmptyBorder;
    
    import com.client.ConnectionManager;
    
    public class MyClientWindow extends JFrame {
    
    	private static final long serialVersionUID = 1L;
    	private JPanel contentPane;
    	private JTextArea txt;
    	private JTextField txtip;
    	private JTextField txtSend;
    
    	public MyClientWindow() {
    		setAlwaysOnTop(true);
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setBounds(100, 100, 450, 300);
    		contentPane = new JPanel();
    		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    		setContentPane(contentPane);
    
    		txt = new JTextArea();
    		txt.setText("准备...");
    
    		txtip = new JTextField();
    		txtip.setText("120.254.12.102");
    		txtip.setColumns(10);
    
    		JButton btnConnect = new JButton("connect");
    		btnConnect.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseClicked(MouseEvent e) {
    				ConnectionManager.getChatManager().connect(txtip.getText());
    			}
    		});
    
    		txtSend = new JTextField();
    		txtSend.setText("hello");
    		txtSend.setColumns(10);
    
    		JButton btnSend = new JButton("send");
    		btnSend.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseClicked(MouseEvent e) {
    				ConnectionManager.getChatManager().send(txtSend.getText());
    				appendText("我说: " + txtSend.getText());
    				txtSend.setText("");
    			}
    		});
    		GroupLayout gl_contentPane = new GroupLayout(contentPane);
    
    		gl_contentPane.setHorizontalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(
    				Alignment.TRAILING,
    				gl_contentPane.createSequentialGroup().addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING)
    						.addGroup(gl_contentPane.createSequentialGroup()
    								.addComponent(txtSend, GroupLayout.DEFAULT_SIZE, 325, Short.MAX_VALUE)
    								.addPreferredGap(ComponentPlacement.RELATED)
    								.addComponent(btnSend, GroupLayout.PREFERRED_SIZE, 109, GroupLayout.PREFERRED_SIZE))
    						.addGroup(Alignment.LEADING,
    								gl_contentPane.createSequentialGroup()
    										.addComponent(txtip, GroupLayout.PREFERRED_SIZE, 294,
    												GroupLayout.PREFERRED_SIZE)
    										.addPreferredGap(ComponentPlacement.RELATED)
    										.addComponent(btnConnect, GroupLayout.DEFAULT_SIZE, 140, Short.MAX_VALUE))
    						.addComponent(txt, GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)).addContainerGap()));
    
    		gl_contentPane.setVerticalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING)
    				.addGroup(gl_contentPane.createSequentialGroup()
    						.addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE)
    								.addComponent(txtip, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
    										GroupLayout.PREFERRED_SIZE)
    								.addComponent(btnConnect))
    						.addPreferredGap(ComponentPlacement.RELATED)
    						.addComponent(txt, GroupLayout.DEFAULT_SIZE, 198, Short.MAX_VALUE)
    						.addPreferredGap(ComponentPlacement.RELATED)
    						.addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING).addComponent(btnSend)
    								.addComponent(txtSend, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
    										GroupLayout.PREFERRED_SIZE))));
    
    		contentPane.setLayout(gl_contentPane);
    	}
    
    	/* 客户端发送的内容添加到中间的txt控件中 */
    	public void appendText(String in) {
    		txt.append("
    " + in);
    	}
    }
    

    ConnectionManager.java

    
    package com.client;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import view.MyClientWindow;
    
    public class ConnectionManager {
    	private ConnectionManager() {
    	}
    
    	private static final ConnectionManager instance = new ConnectionManager();
    
    	public static ConnectionManager getChatManager() {
    		return instance;
    	}
    
    	MyClientWindow window;// 为了能在界面上显示服务器发来的信息,就需要传一个MainWindow的引用进来
    	Socket socket;
    	private String IP;
    	BufferedReader bReader;
    	PrintWriter pWriter;
    
    	public void setWindow(MyClientWindow window) {
    		this.window = window;
    	}
    
    	public void connect(String ip) {
    		this.IP = ip;
    		new Thread() {
    			@Override
    			public void run() {
    				// 实现网络方法
    				try {
    					socket = new Socket(IP, 23456);
                        // 输出流 
    					pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
    					// 输入流
    					bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    					String line = null;
    					// 如果读取数据为空
    					while ((line = bReader.readLine()) != null) {
    						window.appendText("收到: " + line);
    					}
    					// 读完数据之后要关闭
    					pWriter.close();
    					bReader.close();
    					pWriter = null;
    					bReader = null;
    				} catch (UnknownHostException e) {
    					e.printStackTrace();
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    			}
    		}.start();
    	}
    
    	public void send(String sendMsg) {
    		if (pWriter != null) {
    			pWriter.write(sendMsg + "
    ");
    			pWriter.flush();
    		} else {
    			window.appendText("当前链接已经中断...");
    		}
    	}
    }
    

     

    4.运行服务端程序:

    1.把MainServerPro服务端程序导出成jar包

    2.用xshell把jar包上传到linux

    3.程序选用了23456端口,因为部署在阿里云的服务器上,所以要打开阿里云的端口限制

    4.关闭centos7的防火墙

    # service firewalld status; #查看防火墙状态

     (disabled 表明 已经禁止开启启动 enable 表示开机自启,inactive 表示防火墙关闭状态 activated(running)表示为开启状态)

    # service firewalld start;  #开启防火墙

    # service firewalld stop;  #关闭防火墙

    5.启动服务,运行MainServerPro.jar

    有以下三种方式,选择第二种:java -jar xxxxx.jar &  

    java -jar xxxxx.jar  // 当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出

    java -jar xxxxx.jar &   //当前ssh窗口不被锁定,但是当窗口关闭时,程序中止运行。

    nohup Java -jar xxxxxx.jar &  //不挂断运行命令,当账户退出或终端关闭时,程序仍然运行

    6.最后启动客户端,进行聊天

    在点击connect的时候出现报错:

     Exception in thread "Thread-0" java.awt.HeadlessException:  No X11 DISPLAY variable was set, but this program performed an operation which requires it.

    原因是因为服务端程序中有一句JOptionPane.showMessageDialog(null, "one client has connect to 12345 port");

    在linux这种不支持图形界面的环境中 调用图形界面swing有关的代码时,会抛出异常。。。

    解决方法很粗暴,直接把这句代码删掉

    5.源代码下载

    https://download.csdn.net/download/wcc27857285/10784144

  • 相关阅读:
    html表格,table标签
    2-3VRP的基本配置
    6 sys模块
    3 datetime模块
    2 time模块
    1 模块和包的介绍
    12 函数进阶---生成器
    13 函数进阶---迭代器
    10 函数进阶---闭包
    11 函数进阶---装饰器
  • 原文地址:https://www.cnblogs.com/kevinWu7/p/10163457.html
Copyright © 2020-2023  润新知