今天带来的是利用JAVA制作一个简易无GUI的纯控制台聊天室。 实现原理:简易TCP网络编程实现客户端与服务器的交流,IO流读写数据,多线程实现多人交流。
(××为指定用户或管理员名字) ①普通用户功能:可以聊天室群聊,一对一的私聊(使用@××>
); ②管理员功能:可以利用#××
进行踢人,*广告*
发送广告,*广告>
修改默认的广告内容
用的是java.net包里面的ServerSocket创建服务器端口,且设定了管理员特定密码,以及聊天室的普通用户的通用密码 需要开启多线程 一个线程应付一个客户端
package cn.Himit_ZH.ServerLogin;
import cn.Himit_ZH.ServerLogin.CloseUtils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
/**
* 名字:登陆验证服务器
* 用途:实现验证登陆,判断是否为管理员。
* 步骤:
* 创建服务器
* 1、指定端口 使用ServerSocket创建服务器
* 2、阻塞式等待连接 accept
* 3、操作: 输入输出流操作
* 4、释放资源
* IO流实现数据的对接
*
* @author Himit_ZH
*
*/
public class ChatRoomLoginServer {
private static String RoomPwd = "666";//普通用户进入聊天室的密码
private static String AdminPWd = "admin";//管理员进入聊天室的密码
public static void main(String[] args) throws IOException {
System.out.println("-----The login Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(61111);
boolean isRunning =true;;
// 2、阻塞式等待连接 accept
while(isRunning) {
Socket client =server.accept();
System.out.println("-----一个客户端建立了连接-----");
new Thread(new Channel(client)).start();
}
server.close();
}
//一个channel就代表一个客户端
static class Channel implements Runnable{
private Socket client;
//输入流
private DataInputStream dis;
//输出流
private DataOutputStream dos;
private boolean IsAdmin;
public Channel(Socket client) {
this.client = client;
this.IsAdmin = false;
try {
//输入
dis = new DataInputStream(client.getInputStream());
//输出
dos =new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
CloseUtils.close(dos,dis);
}
}
// 判断是否为管理员
public boolean isAdmin() {
return IsAdmin;
}
//接收数据
private String receive() {
String datas ="";
try {
datas = dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
}
return datas;
}
//发送数据
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// 3、操作: 输入输出流操作
String uname ="";
String upwd ="";
//数据分析
String[] dataArray = receive().split("&");
for(String info:dataArray) {
String[] userInfo =info.split("=");
if(userInfo[0].equals("uname")) {
System.out.println("用户名为:"+userInfo[1]);
uname = userInfo[1];
}else if(userInfo[0].equals("upwd")) {
System.out.println("聊天室的密码为:"+userInfo[1]);
upwd = userInfo[1];
}
}
if (uname.contains("admin")&&upwd.equals(AdminPWd)){
send("TRUE_ADMIN");//验证为管理员
this.IsAdmin = true;
}
else {
if (upwd.equals(RoomPwd)) { //成功
send("TRUE_USER");//验证为普通用户
} else { //失败
send("FALSE");
}
}
CloseUtils.close(client,dos,dis);
}
}
}
具体作用:根据指令转发私聊,群聊,实现踢人,发广告,修改广告内容,服务器实现监控连接 需要开启多线程 一个线程应付一个客户端
package cn.Himit_ZH.ServerTransponder;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 名字: 指令识别和内容中转服务器
* 作用: 根据指令转发私聊,群聊,实现踢人,发广告,修改广告内容,服务器实现监控连接
* 利用使用ServerSocket创建服务器
* 阻塞式等待连接 accept
* @author Himit_ZH
*
*/
public class ChatRoomServer {
private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList();
private static Map<String,String> map = new HashMap<>();
private static String advert = "人生苦短,我用python!";
public static void main(String[] args) throws IOException {
System.out.println("-----The Chat Server-----");
map.put("admin1","Himit_ZH");
map.put("admin2","小浩");//此次添加管理员信息
ServerSocket server = new ServerSocket(62222);
while(true) {
Socket client = server.accept();
ChatRoomServer.Channel c = new ChatRoomServer.Channel(client);
all.add(c);
if (c.isAdmin) {
System.out.println("-----一个客户端建立了连接--->[管理员]"+c.name+",目前服务器连接人数为:"+all.size()+"个");
}else{
System.out.println("-----一个客户端建立了连接--->[用户]"+c.name+",目前服务器连接人数为:"+all.size()+"个");
}
(new Thread(c)).start();
}
}
static class Channel implements Runnable {
private DataInputStream dis;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
private boolean isAdmin;
public Channel(Socket client) {
this.client = client;
try {
this.dis = new DataInputStream(client.getInputStream());
this.dos = new DataOutputStream(client.getOutputStream());
this.isRunning = true;
String result = this.receive();
//判断是否为管理员
String []results=result.split("#");
if(results[1].equals("TRUE_ADMIN")){
this.isAdmin = true;
this.name = map.get(results[0]); //获得map容器里面的管理员对应名字
}else {
this.isAdmin = false;
this.name = results[0];//用户名直接获取
}
if (this.isAdmin){
this.send("\t-----尊敬的管理员["+this.name+"],欢迎您的到来!-----\t\n\t-----目前聊天室总人数为:" + (String.valueOf(all.size() + 1))+"个-----\t");
this.send("\t-----温馨提示:使用[@××>]格式可进行私聊,使用#××可踢出对应成员-----\t");
this.send("\t-----温馨提示:使用[*广告*]格式可发系统广告,使用[*广告>××]可踢可修改原定广告内容--\t");
sendOthers("管理员[" + this.name + "]加入<相亲相爱一家人>的聊天室,请大家注意言行!", true);
}else {
this.send("\t-----亲爱的用户[" + this.name + "],欢迎你的到来!-----\t\n\t-----目前聊天室总人数为:" + (String.valueOf(all.size() + 1)) + "个-----\t");
this.send("\t-----温馨提示:使用[@××>]格式可进行私聊-----\t");
sendOthers("用户[" + this.name + "]加入<相亲相爱一家人>的聊天室", true);
}
} catch (IOException var3) {
System.out.println("聊天室服务器初始化失败");
this.release();
}
}
private String receive() {
String msg = "";
try {
msg = this.dis.readUTF();
} catch (IOException var3) {
this.release();
if(this.isAdmin) {
System.out.println("管理员["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
}else{
System.out.println("用户["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
}
}
return msg;
}
private void send(String msg) {
try {
this.dos.writeUTF(msg);
this.dos.flush();
} catch (IOException var3) {
System.out.println("服务器故障,未能成功发送消息!");
this.release();
}
}
private void sendOthers(String msg, boolean isSys) {
char instruction = msg.charAt(0);
boolean flag1 = false;
boolean flag2 = false;
int idx = msg.indexOf(">");
if(msg.equals("*广告*")&&this.isAdmin){
sendOthers("[广告]"+advert,true);
return;
}
if (msg.indexOf("*广告>")==0&&this.isAdmin){
advert = msg.substring(4,msg.length());
send("\t-----系统通知:广告内容修改成功!-----\t");
return;
}
if (instruction == '@'&&idx!=-1) {
String targetName = msg.substring(1, idx);
msg = msg.substring(idx + 1);
for(Channel other: all) {
if (targetName.equals(this.name)){
send("\t-----系统提示:对不起,你不能私聊自己!-----\t");
flag1 = true;
break;
}else {
if (other.name.equals(targetName)) {//目标
if (this.isAdmin) {
other.send("管理员[" + this.name + "]私聊你:" + msg);
} else {
other.send("用户[" + this.name + "]私聊你:" + msg);
}
flag1=true;
break;
}
}
}
if (!flag1){
send("\t-----系统提示:对不起,聊天室并无["+targetName+"]用户或管理员-----\t");
}
}else if(instruction == '#'&&this.isAdmin){
String targetName = msg.substring(1, msg.length());
if (targetName.equals(this.name)){
send("\t-----系统提示:对不起,你不能踢出自己!-----\t");
flag2 = true;
}else {
for (Channel other : all) {
if (other.name.equals(targetName)) {//目标
sendOthers("用户[" + other.name + "]被管理员[" + this.name + "]踢出了聊天室", true);
other.send("#对不起,你被管理员[" + this.name + "]踢出了聊天室!");
other.isRunning = false;
CloseUtils.close(other.dis,other.dos,other.client);//退出
all.remove(other);
flag2 = true;
break;
}
}
}
private String receive() {
String msg = "";
try {
msg = this.dis.readUTF();
} catch (IOException var3) {
this.release();
if(this.isAdmin) {
System.out.println("管理员["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
}else{
System.out.println("用户["+this.name + "]断开了与服务器的连接,目前服务器连接人数为"+all.size()+"人");
}
}
return msg;
}
private void send(String msg) {
try {
this.dos.writeUTF(msg);
this.dos.flush();
} catch (IOException var3) {
System.out.println("服务器故障,未能成功发送消息!");
this.release();
}
}
private void sendOthers(String msg, boolean isSys) {
char instruction = msg.charAt(0);
boolean flag1 = false;
boolean flag2 = false;
int idx = msg.indexOf(">");
if(msg.equals("*广告*")&&this.isAdmin){
sendOthers("[广告]"+advert,true);
return;
}
if (msg.indexOf("*广告>")==0&&this.isAdmin){
advert = msg.substring(4,msg.length());
send("\t-----系统通知:广告内容修改成功!-----\t");
return;
}
if (instruction == '@'&&idx!=-1) {
String targetName = msg.substring(1, idx);
msg = msg.substring(idx + 1);
for(Channel other: all) {
if (targetName.equals(this.name)){
send("\t-----系统提示:对不起,你不能私聊自己!-----\t");
flag1 = true;
break;
}else {
if (other.name.equals(targetName)) {//目标
if (this.isAdmin) {
other.send("管理员[" + this.name + "]私聊你:" + msg);
} else {
other.send("用户[" + this.name + "]私聊你:" + msg);
}
flag1=true;
break;
}
}
}
if (!flag1){
send("\t-----系统提示:对不起,聊天室并无["+targetName+"]用户或管理员-----\t");
}
}else if(instruction == '#'&&this.isAdmin){
String targetName = msg.substring(1, msg.length());
if (targetName.equals(this.name)){
send("\t-----系统提示:对不起,你不能踢出自己!-----\t");
flag2 = true;
}else {
for (Channel other : all) {
if (other.name.equals(targetName)) {//目标
sendOthers("用户[" + other.name + "]被管理员[" + this.name + "]踢出了聊天室", true);
other.send("#对不起,你被管理员[" + this.name + "]踢出了聊天室!");
other.isRunning = false;
CloseUtils.close(other.dis,other.dos,other.client);//退出
all.remove(other);
flag2 = true;
break;
}
}
}
if (!flag2){
send("\t-----系统提示:对不起,聊天室并无["+targetName+"]用户或管理员-----\t");
}
}else {
for(Channel other: all) {
if(!isSys) {
if(other==this) { //自己
if(this.isAdmin) {
send("[管理员]自己:" + msg);
}else{
send("[用户]自己:" + msg);
}
}else {//别人
if (this.isAdmin) {
other.send("[管理员]" + this.name + ":" + msg);//群聊消息
} else {
other.send("[用户]" + this.name + ":" + msg);//群聊消息
}
}
}else {
other.send("\t-----系统通知:"+msg+"-----\t"); //系统消息
}
}
}
}
//释放资源
private void release() {
this.isRunning = false;
CloseUtils.close(dis,dos,client);
//退出
all.remove(this);
sendOthers("用户["+this.name+"]退出了聊天室",true);
}
@Override
public void run() {
while(isRunning) {
String msg= receive() ;
if(!msg.equals("")) {
//send(msg);
sendOthers(msg,false);
}
}
}
}
}
首先先验证登陆,利用TCP,IO流(当然服务器的IP都是“localhost”本机,因为你的服务器也是运行在本机上的,端口就是上面的验证登录服务器设定的端口号)往验证登陆的服务器发送用户名和密码,验证成功后会得到指令TRUE_USER(标识为普通用户)或者是TRUE_ADMIN(标识为管理员),接着中转服务器获取信息初始化,开启对应的用户线程,用户自身也开启两个线程,一个为时时传递消息的发送端线程,一个为时时接受信息的接受端线程。 《1》用户运行的程序(几个用户就启动几个程序)
package cn.Himit_ZH.Client;
import java.io.*;
import java.net.Socket;
/**
* @Author: Himit_ZH
* @Date: 2020/2/20 17:09
* @Description:
* @ProjectName:
*/
public class ChatRoomClient {
public static boolean flag = false;
public static void main(String[] args) throws IOException {
String result = "";
String uname = "";
System.out.println("\t-----The Sign Client-----\t");
System.out.println("\t--<相亲相爱一家人>聊天室---\t");
while (!flag) {
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
System.out.print("\t 请输入昵称:");
uname = console.readLine();
System.out.print("\t 请输入密码:");
String upwd = console.readLine();
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client1 = new Socket("localhost", 61111);
//2、操作: 输入输出流操作
DataOutputStream dos = new DataOutputStream(client1.getOutputStream());
dos.writeUTF("uname=" + uname + "&" + "upwd=" + upwd);
dos.flush();
DataInputStream dis = new DataInputStream(client1.getInputStream());
result = dis.readUTF();
dos.close();
client1.close();
System.out.println("\t-----The Sign Client-----\t");
if (result.equals("FALSE")){
System.out.println("-----密码错误,请重新输入-----");
}
else {
flag =true;
}
//3、释放资源
}
System.out.println("\t-----The Chat Client-----\t");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Socket client2 = new Socket("localhost", 62222);
(new Thread(new Send(client2,uname,result))).start();//Thread1:向服务器发送自己的消息
(new Thread(new Receive(client2))).start();//Thread2:接受服务器的消息(系统消息和其他网友的信息)
}
}
《2》发送端
package cn.Himit_ZH.Client;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 使用多线程封装:发送端
* 作用:获取键盘输入的信息或者指令,往中转服务器发送信息
* @author Himit_ZH
*
*/
public class Send implements Runnable {
private BufferedReader console ;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
private String resultOfAdmin;
public Send(Socket client,String name,String result) {
this.client =client;
console =new BufferedReader(new InputStreamReader(System.in));
this.isRunning = true;
this.resultOfAdmin = result;
this.name = name;
try {
dos =new DataOutputStream(client.getOutputStream());
//发送名称
send(name+"#"+resultOfAdmin);
} catch (IOException e) {
System.out.println("数据输出流初始化错误,请重启");
this.release();
}
}
@Override
public void run() {
while(isRunning) {
String msg = getStrFromConsole();
if(!msg.equals("")) {
send(msg);
}
}
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("与服务器断开连接,消息发送失败!请重启客户端!");
release();
}
}
/**
* 从控制台获取消息
* @return
*/
private String getStrFromConsole() {
try {
return console.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
//释放资源
private void release() {
this.isRunning = false;
CloseUtils.close(dos,client);
}
}
《3》接受端
package cn.Himit_ZH.Client;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 使用多线程封装:接收端
* 作用:接受中转服务器发来的信息,打印到控制台
* @author Himit_ZH
*
*/
public class Receive implements Runnable {
private DataInputStream dis ;
private Socket client;
private boolean isRunning;
public Receive(Socket client) {
this.client = client;
this.isRunning = true;
try {
dis =new DataInputStream(client.getInputStream());
} catch (IOException e) {
System.out.println("数据输入流初始化错误,请重启");
release();
}
}
//接收消息
private String receive() {
String msg ="";
try {
msg =dis.readUTF();
} catch (IOException e) {
System.out.println("未能成功接受到服务器的消息,请重启客户端!");
release();
}
return msg;
}
@Override
public void run() {
while(isRunning) {
String msg =receive();
if (msg.charAt(0) =='#'){
System.out.println(msg.substring(1,msg.length()));
isRunning = false; //收到#指令,关闭线程
}else {
if (!msg.equals("")) {
System.out.println(msg);
}
}
}
}
//释放资源
private void release() {
this.isRunning = false;
CloseUtils.close(dis,client);
}
}
作用是关闭各种资源(IO流和端口)
package cn.Himit_ZH.ServerTransponder;
import java.io.Closeable;
/**
* 名字:工具类
* 作用:关闭各种资源
* @author Himit_ZH
*
*/
public class CloseUtils {
public static void close(Closeable... targets ) {
for(Closeable target:targets) {
try {
if(target!=null) {
target.close();
}
}catch(Exception e) {
System.out.println("资源关闭失败");
}
}
}
}
登陆界面 默认的管理员账号为admin1或者admin2 密码都为admin 用户账号昵称可以随便设置 密码为聊天室房间默认密码666 要修改的话在步骤②中的代码找,有备注了
群聊私聊
两个广告指令
踢人