Java学习记录——简单的多线程群聊

Java学习记录——简单的多线程群聊

最近学习了Java中的网络编程,实现了一个简单的多线程群聊。也参考了一些例子。记录一下

思路:

  1. 建立客户端(Client.java),服务端(Server.java)类。客户端可以新建用户并且开启聊天窗口,每新建一个用户 服务端就向线程池添加一个新线程。在服务端建立一个消息池便于发消息给每位用户。先运行Server再运行Client

  2. Server:建立GUI界面。建立ArrayList线程池(用户池)和消息池。建立一个SocketServer,随后循环建立Socket.accept作为接收器。注意:accept会堵塞线程直到下一个客户端连接。连接一个客户线程池就启动一个专门的线程 。 通过DataInputStream和readUTF的方式接收来自客户端的消息。通过DataOutputStream和writeUTF的方式遍历用户池,给每个客户发送消息。

  3. Client:
    建立GUI界面。新建用户按钮点击时,创建Socket连接服务器。创建聊天框,包含接收框、发送框、发送按钮。接收框自动接收消息,发送按钮点击时给每位用户发消息。
    通过DataInputStream和readUTF的方式接收来自服务端的消息。
    通过DataOutputStream和writeUTF的方式给服务端发送消息。

代码如下:
Server:

import java.awt.BorderLayout;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import javax.swing.*;

public class Server implements Runnable{
	ServerSocket server;
	Socket s;
	JTextArea area;
	JFrame jf;
	ArrayList <String> outAll= new ArrayList<>();//存放用户消息
	ArrayList<ServerThread> threads = new ArrayList<>();//存放用户消息
	Server(){
		//界面
		jf = new JFrame("服务器消息池");
		jf.setSize(500, 600);
        jf.setLayout(null);
		area=new JTextArea();
        area.setBounds(50, 10, 400,600);
        area.setEditable(false);
        area.setLineWrap(true);        //激活自动换行功能 
        jf.add(area);
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
	}
	public void run() {
		Socket s = null;
		while(true){	
				try {
					Thread.sleep(1000);//每秒判断有无客户接入
				} catch (InterruptedException e2) {
					e2.printStackTrace();
				}
	            try{  
	            	server=new ServerSocket(8765);
	            }
	            catch(IOException e1){
	                 System.out.println("正在监听");   //ServerSocket对象不能重复创建
	            } 
	            try{  
	            	 System.out.println("等待一下个客户连接");
	            	 s=server.accept();
	                 System.out.println("新客户的地址:"+s.getInetAddress());
	            }
	            catch (IOException e){
	                 System.out.println("正在等待客户");
	            }
	            
				if(s!=null) {
					ServerThread serverThread = new ServerThread(s,this);
					threads.add(serverThread);
	                new Thread(serverThread).start();     //连接一个客户线程池就启动一个专门的线程 
				}		
			}
	}
	class ServerThread implements Runnable{
		Socket socket;
	    DataOutputStream out=null;
	    DataInputStream in=null;
	    Server server = null;
		 ServerThread(Socket s,Server server){
		       socket = s;
		       this.server = server;	   
		       try{   
		    	   in=new DataInputStream(socket.getInputStream());
		           out=new DataOutputStream(socket.getOutputStream());
		       }
		       catch (IOException e){
		    		System.out.println("创建输入流失败");
		       }
		    }  
		public void run() { 			
			while(true){
				try {
					if(!server.outAll.isEmpty()){
						//发送消息给每个客户						
						String sent = server.outAll.get(server.outAll.size()-1);
						area.append(sent+"\r\n");		
						for(int j = 0 ; j < threads.size(); j++){			
							try {
								threads.get(j).out.writeUTF(sent);
								//不然就只有本线程的客户收的到
								out.flush();
							} catch (IOException e) {
								System.out.println("发送失败给客户"+j+"失败");
							}
						}
					}
					String msg = in.readUTF();//堵塞线程直到该线程读取消息
					server.outAll.add(msg);		
				}catch (IOException e) {
					System.out.println("接受失败,断开服务器");
				}
			}			
		}
	}
	
	public static void main(String args[]){
		new Thread(new Server()).start();
	}
}

Client:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.*;

public class Client implements Runnable{
	void createUsers(String name){
		new Thread(new Users(name)).start();//创建新用户
	}
	public void run() {
		//建立客户端,用以新建用户
		JFrame client = new JFrame("客户端");
		client.setBounds(690,400,400, 300);
		JButton register= new JButton("新建用户");
		register.setBounds(150,100, 100, 40);
		JLabel jl = new JLabel("输入姓名:");
		jl.setBounds(80,50,70,30);
		JTextField jt = new JTextField();
		jt.setBounds(150, 50,120, 30);
		client.setLayout(null);
		client.add(jl);
		client.add(jt);
		client.add(register);
		client.setVisible(true);
		register.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//点击创建用户,建立Client的子线程
				new Thread(new Users(jt.getText())).start();
				jt.setText("");
			}			
		});
	}
	
	class Users implements Runnable{
		String name;
		Socket s ;
		JButton send2 ;
		OutputStream os;
		DataInputStream dis;
		JTextArea jtC ;
		JTextArea jt;
		JLabel jl1;
		JLabel jl2;
		JButton send ;
		DataOutputStream dos = null;	
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		String getCurrentTime() {
			return sdf.format(new Date());
		}

		Users(String name){
			this.name = name;
		//构造GUI界面******************
			JFrame client = new JFrame(name);
			client.setBounds(690,400,360, 600);
			send = new JButton("send");
			send.setBounds(130,450, 80, 40);
			client.setLayout(null);	
			client.add(send);
			jl1 = new JLabel("消息框");
			jl2 = new JLabel("发送框");
			jl1.setBounds(150,10,360,30);
			jl2.setBounds(150,270,360,30);
			client.add(jl1);
			client.add(jl2);
			jtC = new JTextArea();
			jtC.setBounds(10, 50,320, 200);
			jtC.setLineWrap(true) ;
			jtC.setEditable(false);//设置不可编辑,只可展示	
			client.add(jtC);
			jt = new JTextArea();
			jt.setBounds(10, 320,320, 150);
			jt.setLineWrap(true) ;
			client.add(jt);
			 //在文本框上添加滚动条
	        JScrollPane jsp = new JScrollPane(jtC);
	        JScrollPane jsp2 = new JScrollPane(jt);
	        //设置矩形大小.参数依次为(矩形左上角横坐标x,矩形左上角纵坐标y,矩形长度,矩形宽度)
	        jsp.setBounds(10, 50, 320, 200);
	        jsp2.setBounds(10, 320, 320, 100);
	        //默认的设置是超过文本框才会显示滚动条,以下设置让滚动条一直显示
	        jsp.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	        jsp2.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	        //把滚动条添加到容器里面
	        client.add(jsp2);
	        client.add(jsp);    //加入到JFrame
			client.setVisible(true);
		//构造GUI界面*****************************
			
			//设置发送消息
			send.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {		
						String msg = jt.getText();
		                jt.setText(""); 
						try {				
							dos = new DataOutputStream(s.getOutputStream());
							//打开输出流
							dos.writeUTF(getCurrentTime()+"\n"+name+": "+msg);
							//使用writeUTF发送字符串,发完之后清空输入框
							 jt.setText("");
							 dos.flush();
						} catch (IOException e1) {
							System.out.println("发送失败");
						}		
				}			
			});
		}
		
		public void run() {
			try {
				s = new Socket("localhost",8765);	
			} catch (Exception e) {
				System.out.println("创建客户端失败");
				return;
			}
			//打开输入流
			InputStream is = null;
			try {
				is = s.getInputStream();
			} catch (IOException e1) {
				System.out.println("创建输入流失败");
			}
			//把输入流封装在DataInputStream中
			dis = new DataInputStream(is);	
			while(true){
				try{   
					String msg = dis.readUTF();//堵塞状态,直到接收到信息
					jtC.append(msg+"\r\n");	//接受消息池的所有消息
				 }
			     catch (IOException e){
			           System.out.println("网络连接中断");     
			           return;
			     }		
			}
		}
	}
	public static void main(String args[]){
		new Thread(new Client()).start();
	}
}

运行结果截图:

新建客户
聊天窗口

猜你喜欢

转载自blog.csdn.net/weixin_43363255/article/details/85065426
今日推荐