网络通信的学习(二)-------编写聊天室程序

多线程服务器

原理

  • ServerSocket+Thread
总框架
服务器利用多线程并发实现服务器同时处理多个客户端的效果
整体框架图
服务器和单一客户机交互图
客户端与服务器通过套接字输入输出流传递信息,服务器将接收的信息再传递给其他已登录客户端
服务器和单一客户机交互图

服务端代码

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
//服务器代码
public class Server {
	private HashSet<Socket> allSocket;//创建保存所有客户端的集合
	private ServerSocket server;//创建服务器套接字
	
	//创建构造方法
	public Server() {
		try {
			server=new ServerSocket(5214);//实例化服务器套接字
			System.out.println("服务器就绪···");
		} catch (IOException e) {
			e.printStackTrace();
		}
		allSocket=new HashSet<>();//实例化HashSet
		
	}
	
	//服务器启动方法
	public void startServer() throws IOException {
		//循环获取客户端连接Socket
		while(true) {
			Socket socket=server.accept();//获取连接
			
			System.out.println("用户进入聊天室");
			allSocket.add(socket);//将对象放入集合
			ServerThread t=new ServerThread(socket);//创建线程
			t.start();//开始线程
		}
	}
	
	//线程内部类,读取用户发送的消息再将信息发送给所有客户端
	private class ServerThread extends Thread{
		Socket socket;//声明客户端套接字
		//线程构造方法
		public ServerThread(Socket socket) {
			this.socket=socket;
		}
		
		public void run() {
			//读取用户发送的消息
			BufferedReader br=null;//声明字符输入流
			try {
				br=new BufferedReader(new InputStreamReader(socket.getInputStream()));//获取客户端输入字符流
				while(true) {
					String str=br.readLine();
					if(str.contains("EXIT")) {//如果客户端输入的字符串("%EXIT%:用户名%")中有“EXIT”,则客户端退出聊天室
						String temp=str.split(":")[1]+"退出聊天室";
						sendMessageToAllClient(temp);
						allSocket.remove(socket);//删除推出的套接字
						socket.close();//关闭套接字
						System.out.println(temp);
						break;//停止线程
					}
					sendMessageToAllClient(str);//将信息发送给所有客户端
				}
				//若服务器中没有客户端,则关闭程序
				if(allSocket.isEmpty()) {
					System.exit(0);//关闭程序
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	} 
	
	//将信息发送给所有客户端
	private void sendMessageToAllClient(String str) {
		for (Socket socket : allSocket) {
			try {
				BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//获取输出字符流
				bw.write(str);//写入字符串
				bw.newLine();//换行
				bw.flush();//刷新
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}
	
	
	
	public static void main(String[] args) {
		Server s=new Server();
		try {
			s.startServer();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

}

客户端代码

定义客户端套接字

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
//客户端套接字
public class Client {
	Socket socket;//套接字对象
	BufferedWriter bw;//输出字符流
	BufferedReader br;//输入字符流
	
	public Client(String ip,int port) {
		try {
			socket=new Socket(ip, port);//创建套接字
			bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//创建该套接字下的输出字符流
			br=new BufferedReader(new InputStreamReader(socket.getInputStream()));//创建该套接字下的输入字符流
			
		} catch (UnknownHostException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	
	//客户端发消息
	public void sendMessage(String message) {
		try {
			bw.write(message);//写出message
			bw.newLine();//换行
			bw.flush();//刷新
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
	}
	
	//客户端收消息
	public String receiveMessage() {
		String message=null;
		try {
			message=br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return message;
	}
	
	//关闭客户端
	public void closeClient() {
		try {
			socket.close();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

}

连接服务器窗体

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.border.EmptyBorder;

/*
 * 连接服务器窗体
 * */

@SuppressWarnings("serial")
public class LinkServerFrame extends JFrame{
	private JPanel contentPane;//面板
	private JLabel IP=new JLabel("IP地址");//IP地址标签
	private JLabel user=new JLabel("用户名");//用户名标签
	private JTextField Ipaddress=new JTextField("127.0.0.1", 20);//IP地址文本框
	private JTextField userName=new JTextField(20);//用户名文本框
	private JButton jb=new JButton("确定");//确定按钮
	
	public LinkServerFrame() {
		setTitle("连接服务器");
		setResizable(false);//窗口大小不可变
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(400,400,300,150);
		//面板
		contentPane=new JPanel();
		contentPane.setLayout(new FlowLayout(1, 10, 10));//使用流布局
		setContentPane(contentPane);
		//IP地址
		IP.setSize(60, 20);
		contentPane.add(IP);
		//IP地址文本框
		contentPane.add(Ipaddress);
		//用户名
		user.setSize(40,20);
		contentPane.add(user);
		//用户名文本框
		contentPane.add(userName);
		//确定按钮
		contentPane.add(jb);
		//添加按钮事件监听器
		jb.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				do_Link_actionPerformed(e);
			}
		});
		
		setVisible(true);
	}
	
	
	protected void do_Link_actionPerformed(ActionEvent e) {
		if(!Ipaddress.getText().equals("")&&!userName.getText().equals("")) {//当文本框内内容不为空时
			dispose();//销毁当前窗体
			new ClientFrame(Ipaddress.getText().trim(),userName.getText().trim());
			
		}else {
			JOptionPane.showMessageDialog(null, "文本框内容不能为空","警告",JOptionPane.WARNING_MESSAGE);
		}
	}


	public static void main(String[] args) {
		LinkServerFrame link=new LinkServerFrame();
	}

}

窗口展示

在这里插入图片描述

用户发送接收消息窗口

import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.*;
@SuppressWarnings("serial")
public class ClientFrame extends JFrame{
	private String uername;//用户名
	private JPanel contentPane;//面板
	private JTextArea text;;//文本域
	private JTextField message;//文本框
	private JButton send;//发送按钮 
	private JLabel jl;//用户名标签
	Client client;//client对象
	
	public ClientFrame(String ip, String uer) {
		this.uername=uer;
		init();//添加窗体初始化内容
		addListener();//添加监听
		
		client=new Client(ip, 5214);//创建client对象
		ReadMessageThread t=new ReadMessageThread();
		t.start();//开始收消息线程
	}
	
	//收消息线程
	private class ReadMessageThread extends Thread{
		public void run() {
			while(true) {
				String str=client.receiveMessage();//客户端收消息
				text.append(str+"\n");//将消息加入文本域
			}
		}
	}

	private void addListener() {
		//发送按钮事件监听
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Date date=new Date();
				SimpleDateFormat sf=new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
				client.sendMessage(uername+" "+sf.format(date)+"\n"+message.getText());//客户端发消息
				message.setText(" ");//清空文本框
			}
		});
		//窗口事件监听
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent atg0) {
				int op=JOptionPane.showConfirmDialog(ClientFrame.this, "确定要退出聊天室吗?","确定",JOptionPane.WARNING_MESSAGE);
				if(op==JOptionPane.YES_OPTION) {
					client.sendMessage("%EXIT%:"+uername);
					//客户端关闭停顿(给服务器缓冲时间)
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
					}
					client.closeClient();//关闭客户端
					System.exit(0);//关闭程序
				}
			}
		});
		
	}

	private void init() {
		setTitle("客户端");
		setResizable(false);
		setBounds(400, 400, 450, 296);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//面板
		contentPane=new JPanel();
		contentPane.setLayout(new FlowLayout(1, 5, 5));//使用流布局
		setContentPane(contentPane);
		
		//文本域
		text=new JTextArea(12,40); 
		JScrollPane sp=new JScrollPane(text);
		contentPane.add(sp);
		
		//用户名标签
		jl=new JLabel(uername);
		jl.setFont(new Font("微软雅黑", Font.PLAIN,18));
		contentPane.add(jl);
		
		//文本框
		message=new JTextField(25);
		contentPane.add(message);
		
		//发送按钮 
		send=new JButton("发送");
		send.setSize(40, 20);
		contentPane.add(send);
		
		setVisible(true);
	}

}

窗口展示

在这里插入图片描述

结果展示

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值