Java——实现聊天室

学习Java的每一个人都知道,聊天室是每一个程序员都要过手的项目,根据要求的不同,聊天室的实现可易可难。

我今天的聊天室程序主要实现的功能是:

1、私聊功能

2、群聊功能

3、查看成员列表功能

4、退出聊天室功能

5、发送文件功能

内容比较简单,是学完JavaSE的一次知识总结,没有用到界面等,都是在控制台上进行输出的。希望能给大家提供借鉴。代码如下,程序在最后打包,希望对于初学者的同学

package org.westos.client;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

import org.westos.client.Config.Config;
import org.westos.util.InputAndOutputUtil;
import org.westos.util.InputUtil;

/**
 * 客户端
 * @author 虎
 *
 */
public class Client {

	private static Socket s;
	private static InputStream is;
	private static OutputStream os;
	private static Scanner sc;
	private static Config config;
	private static  String clientName;
	public static void main(String[] args) {
		 sc = new Scanner(System.in);
		try {
			 s = new Socket("LocalHost", 9999);
			 os = s.getOutputStream();
			 is = s.getInputStream();
			 //注册------->不停的去注册
			 while(true) {
				 System.out.println("请设置您的昵称");
				 clientName = sc.nextLine();
				 os.write(clientName.getBytes());
				 os.flush();
				 //读取服务器的反馈
				 byte[] result = new byte[1024];
				 int len = is.read(result);
				 String str = new String(result, 0, len);
				 if(str.equals("yes")) {
					 System.out.println("注册成功");
					 break;
				 }
				 else {
					 System.out.println("请重新输入");
				 }
			 }
			ClientReadThread thread = new  ClientReadThread(is);
			thread.start();
			 boolean isRun = true;
			//开始聊天
			 while(isRun) {
				 System.out.println("请选择要进行的操作: 1,私聊  2,公聊  3,在线列表 4 ,退出 聊天 5,发送文件6,下载文件");
				 int chioce =InputUtil.inputIntType(new Scanner(System.in));
				 switch (chioce) {
				case 1:
					System.out.println(1);
					privateChat();
					break;
				case 2:	
					System.out.println(2);
					publicChat();
					break;
				case 3:	
					System.out.println(3);
					onlineList();
					break;
				case 4:	
					System.out.println(4);
					exitChat();
					isRun = false;
					break;
				case 5:	
					System.out.println(5);
					sendFile();
					break;
				case 6 :	
					System.out.println(6);
					downloadFile();
					break;

			 }
			 }
		}
		
		 catch (Exception e) {
			e.printStackTrace();
		}
		System.exit(0);
		
	}
	private static void downloadFile() {
		// TODO Auto-generated method stub
		
	}
	private static void sendFile() throws IOException {
		//现主要实现私发
		System.out.println("请输入接收文件的用户姓名:");
		String receiver  = sc.nextLine();
		System.out.println("请选择您要发送的文件:(输入具体路径)");
		String filePath = sc.nextLine();
		//构建文件
		File file = new File(filePath);
		String fileName = file.getName();
		long fileLength = file.length();
		String msg = clientName + ":" + receiver +":" + (fileName + "#" + fileLength) + ":" + Config.MSG_SEBDFILE+":";
		//使用内存操作流输入
		byte[] msgBytes = msg.getBytes();
		//空字节数组
		byte[] emptyBytes = new byte[1024*10-msgBytes.length] ;
		//利用工具将文件转化为字节数组
		byte[] fileBytes = InputAndOutputUtil.readFile(filePath);
		//System.out.println("经过工具后,文件变化为字节的大小为:"+fileBytes.length);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		baos.write(msgBytes);
		baos.write(emptyBytes);
		baos.write(fileBytes);
		byte[] allBytes = baos.toByteArray();
		//System.out.println("客户端发出的数据总共字节大小:"+allBytes.length);
		//通过输出流输出到服务器中
		os.write(allBytes);
		os.flush();
	}
	private static void exitChat() throws IOException {
		//退出聊天
		System.out.println("谢谢使用,再见");
		String msg = clientName+ ":" + "null" + ":" + "null" +":"+ Config.MSG_EXIT;
		os.write(msg.getBytes());
		//关闭 
	}
	private static void onlineList() throws IOException {
		System.out.println("******当前在线的好友有******");
		String msg = clientName + ":" + "null" + ":" + "null" + ":" +Config.MSG_ONLINELIST; 
		os.write(msg.getBytes());
	}
	@SuppressWarnings("static-access")
	private static void publicChat() throws IOException {
		System.out.println("----------------欢迎您公聊模式--------------");
		System.out.println("请输入您要发的消息:");
		String msg = sc.next();
		//没有发送者用null来代替
		//消息格式: 发送者:接受者:所发信息:消息类型
		msg = clientName + ":"+"null"+":" + msg + ":" + config.MSG_PUBLICCHAT;
		os.write(msg.getBytes());
	}
	
	@SuppressWarnings("static-access")
	private static void privateChat() throws IOException {
		System.out.println("----------------欢迎您进入私聊模式--------------");
		//消息个数:接受者+发送消息+消息类型
		System.out.println("请输入接受者的姓名:");
		String receiver = sc.nextLine();
		System.out.println("请输入您要给他(她)话:");
		String msg = sc.next();
		//消息格式: 发送者:接受者:所发信息:消息类型
		msg = clientName + ":" + receiver +":"+ msg +":"+ config.MSG_PRIVATECHAT ;
		os.write(msg.getBytes());
	}
}


package org.westos.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.westos.client.Config.Config;
import org.westos.util.InputAndOutputUtil;

/**
 * 客户端读取输入流的线程
 * @author 虎
 *
 */
public class ClientReadThread extends Thread{

	private InputStream is;
	public ClientReadThread(InputStream is) {
		super();
		this.is = is;
	}

	
	@Override
	public void run() {

			try {
				while(true) {
				byte[] bytes = new byte[1024*10];
				int len = is.read(bytes);
				String infomation = new String(bytes, 0, len);
				String[] info = infomation.split(":");
				String clientName = info[0];
				String receiver = info[1];
				String msg = info[2];
				int infoType =Integer.parseInt(info[3]);
				//私聊模式
				//System.out.println(infomation);
				if(infoType == (Config.MSG_PRIVATECHAT)) {
					System.out.println(clientName+"@你说:"+msg);
				}
				else if(infoType == (Config.MSG_PUBLICCHAT)) {
					System.out.println(clientName+"对大家说:"+msg);
				}
				else if (infoType == Config.MSG_ONLINE) {
					System.out.println(clientName+msg);
				}
				else if(infoType == (Config.MSG_ONLINELIST)) {
					System.out.println(msg);
				}
				else if(infoType == Config.MSG_EXIT) {
					System.out.println(msg);
				}
				else if(infoType == Config.MSG_SEBDFILE) {
					String[] file = msg.split("#");
					String fileName = file[0];
					long fileLength = Long.parseLong(file[1]);
					System.out.println(clientName+"给您发来一个文件:"+ fileName + "文件大小为:"+fileLength);
					byte[] memoryBytes = new byte[1024];
					int memoryLength = 0;
					//int len1 = infomation.getBytes().length;
					//不断的去读取
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					int len1 = infomation.getBytes().length;
					//System.out.println("读取的消息的长度:"+len1);
					while (true) {
						int len2= is.read(memoryBytes);
						baos.write(memoryBytes, 0, len2);
						//实际长度
						memoryLength += len2;
						//判断截止标记
						//System.out.println(memoryLength);
						//System.out.println(fileLength);  31879
						if (fileLength == memoryLength) {
							break;
						}
						//System.out.println("ha2");
					}
					//System.out.println("最终的长度:"+memoryLength);
					byte[] fileBytes = baos.toByteArray();
					//调用工具
					boolean flag = InputAndOutputUtil.writeFile("D:/"+fileName, fileBytes);
					//针对不同情况进行判断
					//System.out.println("ha3");
					if (flag) {
						System.out.println("文件接受成功!保存在D:/"+fileName+"中,请前往查看");
						break;
					}
					else {
						System.out.println("文件接受失败!");
					}
				}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		
	}

}
package org.westos.server;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

/**
 * 
 * @author 代虎
 *
 */
public class Server {

	private static ServerSocket ss;
	private static Socket s;
	private static InputStream is;
	private static OutputStream os;
	static HashMap<String, Socket> map = new HashMap<String, Socket>() ;
	public static void main(String[] args) {
		int num = 1; 
		
			try {
				ss = new ServerSocket(9999);
				System.out.println("服务器已启动...");
				while (true) {
				s = ss.accept();
				is = s.getInputStream();
				os = s.getOutputStream();
				System.out.println("第"+ (num++)+"个客户端接入");
				new SaveClientThread(s, map).start();;
				
				}
				} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
}

package org.westos.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;

import org.westos.server.config.Config;

/**
 * 存取客户的信息
 * @author 代虎
 *
 */
public class SaveClientThread extends Thread{

	private Socket s;
	private InputStream is;
	private OutputStream os;
	private String clientName;
	private Config config;
	private HashMap<String, Socket> map = new HashMap<String,Socket>();
	
	public SaveClientThread(Socket s, HashMap<String, Socket> map) {
		this.s = s;
		this.map = map;
	}

	@Override
	public void run() {
		while(true) {
			byte[] name = new byte[1024];
			try {
				is = s.getInputStream();
				os = s.getOutputStream();
				int len = is.read(name);
				clientName = new String(name, 0, len);
				if(map.containsKey(clientName)) {
					os.write("no".getBytes());
				}
				else {
					map.put(clientName, s);
					os.write("yes".getBytes());
					System.out.println("当前在线好友:");
					for(String client:map.keySet()) {
						System.out.println(client);
					}
					break;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
			//给客户端反馈上线的消息
			for(String userName:map.keySet()) {
				//本人上线不需要踢提醒自己
				if(userName.equals(clientName)) {
					continue;
				}
				else {
					Socket socket = map.get(userName);
					String msg = clientName+":"+ userName +":"+ "上线了" +":"+ Config.MSG_ONLINE;
					try {
						socket.getOutputStream().write(msg.getBytes());
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				//System.out.println("发送成功");
			}
		new ServerTransmitThread(s, map).start();;
		
	}
}
package org.westos.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;

import org.westos.server.config.Config;

public class ServerTransmitThread extends Thread{
	private OutputStream os;
	private InputStream is;
	private Socket s;
	private  HashMap<String, Socket> map = new HashMap<String, Socket>() ;
	
	public ServerTransmitThread(Socket s, HashMap<String, Socket> map) {
		super();
		this.s = s;
		this.map = map;
	}
	@SuppressWarnings("unused")
	@Override
	public void run() {
		try {
			is = s.getInputStream();
			while(true) {
				//先定义50kb
				byte[] bytes = new byte[1024*10];
				int len= is.read(bytes);
				String infomation = new String(bytes, 0, len);
				String[] info = infomation.split(":");
				String sendName = info[0];
				String receiver = info[1];
				String context = info[2];
				int infoType =Integer.parseInt(info[3]);
				//公聊
				if(infoType == (Config.MSG_PRIVATECHAT)) {
					Socket socket = map.get(receiver);
					socket.getOutputStream().write(infomation.getBytes());
				}
				//私聊
				else if(infoType == (Config.MSG_PUBLICCHAT)) {
					for(String str:map.keySet()) {
						Socket socket = map.get(str);
						socket.getOutputStream().write(infomation.getBytes());
					}
				}
				//在线列表
				else if(infoType == Config.MSG_ONLINELIST) {
					Socket socket = map.get(sendName);
					int num = 1;
					StringBuffer sb = new StringBuffer();
					for(String str:map.keySet()) {
						sb.append(num++).append("、").append(str).append("\n");
					}
					String msg  = sendName +":"+ receiver+":" + (sb.toString())+":" + Config.MSG_ONLINELIST;
					Socket socket2 = map.get(sendName);
					socket2.getOutputStream().write(msg.getBytes());
				}
				//退出聊天室
				else if (infoType == Config.MSG_EXIT) {
					//在map中移除该用户
					map.remove(sendName);
					String msg = sendName+"下线了";
					for(String str:map.keySet()) {
						msg = "null"+ ":"+ str + ":" + msg + ":" + infoType;
						Socket socket = map.get(str);
						socket.getOutputStream().write(msg.getBytes());
					}
				}
				//发送文件
				else if (infoType == Config.MSG_SEBDFILE) {
					//System.out.println("发送到服务器端文件总共字节大小"+infomation.getBytes().length);
					Socket socket = map.get(receiver);
					String[] file = context.split("#");
					String fileName = file[0];
					long fileLength = Long.parseLong(file[1]);
					String msg = sendName + ":" + receiver +":" + context+ ":" + Config.MSG_SEBDFILE+":";
					byte[] msgBytes = msg.getBytes();
					byte[] emptyBytes = new byte[1024*10-msgBytes.length] ;
					//读取文件
					byte[] memoryBytes = new byte[1024];
					int memoryLength = 0;
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					//System.out.println("读取开始了吗?");
					//不断的去读取
					while (true) {
						//System.out.println("1读取开始了");
						int len2= is.read(memoryBytes);
						//System.out.println("2到这了吗");
						baos.write(memoryBytes, 0, len2);
						//baos.flush();
						//实际长度
						memoryLength += len2;
						//System.out.println("3到这了吗");
						//判断截止标记
						//System.out.println("1:"+memoryLength);
						//System.out.println("2:"+len2);
						if (fileLength == memoryLength) {
							break;
						}
						//System.out.println("3");
					}
					byte[] fileBytes = baos.toByteArray();
					baos.reset();
					//重置,继续使用该输出流对象写数据到内存中
					//System.out.println("4");
					baos.write(msgBytes);
					baos.write(emptyBytes);
					baos.write(fileBytes);
					//内存中已经有这些数据,需要拼接成一个大的字符数组发过去
					byte[] allBytes = baos.toByteArray();
					//System.out.println("发送的长度:"+allBytes.length);
					socket.getOutputStream().write(allBytes);
				}
			}
		} catch (IOException e) {
		//	e.printStackTrace();
		}
	}

}

工具包:
package org.westos.server.config;
/**
 * 常量(相当于客户端与服务器之间的人协议一样)
 * @author 代虎
 *
 */
public class Config {

	public static final int MSG_PRIVATECHAT = 100;//私聊
	public static final int MSG_PUBLICCHAT = 200;//公聊
	public static final int MSG_ONLINE = 300;//上线
	public static final int MSG_ONLINELIST = 400;//在线列表
	public static final int MSG_EXIT = 500; //退出
	public static final int MSG_SEBDFILE= 600;//发送文件
	public static final int MSG_DOWNLOAD= 700;//下载文件
	
}

package org.westos.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class InputAndOutputUtil {
	public static byte[] readFile(String path) {
		File file = new File(path);
		// 数组用来保存读取的数据 相当于水池
		byte datas[] = null;
		if (!file.exists()) {
			datas = null;
		} else {
			try {
				// 字节数组输出流 用来往内存中写字节数组 可以用来拼接字节数组
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				// 创建文件输入流
				FileInputStream fis = new FileInputStream(file);
				// 用来保存每次读的数据 相当水瓢(每次读1024字节 但是不一定每次能读这么多 实际读取的长度用len保存)
				byte data[] = new byte[1024 * 1024];
				// 用来保存每次读取的字节大小
				int len = 0;
				// 不断的读取 直到数据读完
				while ((len = fis.read(data)) > 0) {
					// 把每次读入的数据 存放在字节数组流的内存中
					baos.write(data, 0, len);
				}
				// 把字节数组流中的数据转为字节数组
				datas = baos.toByteArray();
				baos.flush();
				baos.close();
				// 关闭流
				fis.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return datas;
	}

	public static boolean writeFile(String path, byte datas[]) {
		try {
			File file = new File(path);
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(datas);
			// 倾倒关闭
			fos.flush();
			fos.close();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
}

package org.westos.util;

import java.util.Scanner;

public class InputUtil {
	
	//一直要我们录入整数为止
	public static int inputIntType(Scanner sc) {
		int choose = 0;
		while (true) {
			try {
				//录入用户输入的整数
				choose = sc.nextInt();
				break;
			} catch (Exception e) {
				sc = new Scanner(System.in);
				System.out.println("输入的类型不正确,请重新输入:");
			}
		}
		return choose;
	}
}




全部评论

相关推荐

10-15 16:27
门头沟学院 C++
LeoMoon:建议问一下是不是你给他付钱😅😅
点赞 评论 收藏
分享
评论
点赞
1
分享
牛客网
牛客企业服务