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;
}
}