这套聊天系统是本人为了完成计算机网络大作业而独自编写的,百分百原创,在提交完大作业后便拿出来给大家分享。
其主要功能的使用和代码的解释都在注释里,如有写的不好的地方请多见谅。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
//服务器端
public class Server {
private static HashMap<String,ServerSocket> ConnectPort; //监听端口池,用于让服务器知道有哪些端口号正在被使用,便于管理端口资源
private static HashMap<String,Socket> PublicConnect; //公共聊天室连接池
private static Set<Map.Entry<String,Socket>> PublicConnectSet; //公共连接池集合,用于轮询用户进行广播
private static ServerSocket listenSocket; //8080公共端口,用于给用户分发专属端口
private static Scanner scanner;//控制台输入
private static int modeflag; //服务器运行模式标志
private static int privateflag; //私人聊天室建立完成标志,用于释放公共聊天线程的堵塞状态
//主线程,负责服务器启动和模式切换
public static void main(String[] args) throws IOException {
ServerInit();
new GetConnectionThread().start();
new PublicBroadcastThread().start();
while (true){
String command = scanner.nextLine();
ModeChange(command);
}
}
//服务器全局变量初始化
public static void ServerInit() throws IOException {
listenSocket = new ServerSocket(8080,10);
ConnectPort = new HashMap<>();
PublicConnect = new HashMap<>();
PublicConnectSet = PublicConnect.entrySet();
scanner = new Scanner(System.in);
modeflag = 2;//默认为监控模式
privateflag = 0;
if (modeflag == 2){
System.out.println("TCP服务器初始化成功");
}
}
//模式切换
public static void ModeChange(String command){
if("mode".equals(command)){
System.out.println("模式选择:1、静默模式 2、监控模式");
modeflag = scanner.nextInt();
System.out.println("现在是" + modeflag + "模式");
}
}
//通过8080端口分发专属端口的方式与客户端建立连接(TCP)
public static void BuildNewConnect(ServerSocket listenSocket) throws Exception {
Socket TCPsocket = null;
try {
byte[] bytes = new byte[1024];
ServerSocket ConnectSocket = new ServerSocket();
ConnectSocket.bind(new InetSocketAddress("127.0.0.1",0));//端口0表示让操作系统随机分配一个空端口
int port = ConnectSocket.getLocalPort();
String portstr = String.valueOf(port);
TCPsocket = listenSocket.accept();
InputStream inputStream = TCPsocket.getInputStream();
OutputStream outputStream = TCPsocket.getOutputStream();
TCPsocket.setSoTimeout(10000);//设置tcp连接超时
int num = inputStream.read(bytes);
String ConnectName = new String(bytes,0,num);//获取连接的用户用户名,便于对该连接进行管理
bytes = portstr.getBytes();
outputStream.write(bytes);//向客户端发送新端口号
TCPsocket.close();
AddPublicConnectPool(ConnectName,ConnectSocket);
}
catch (Exception e){
TCPsocket.close();
}
}
//将客户端的Socket入公共聊天室连接池,端口号入监听端口池
public static void AddPublicConnectPool(String ConnectName,ServerSocket ConnectSocket) throws IOException {
Socket dataSocket = ConnectSocket.accept();
ConnectPort.put(ConnectName,ConnectSocket);
PublicConnect.put(ConnectName,dataSocket);
if (modeflag == 2){
System.out.println(ConnectName + "与服务器建立通讯");
}
}
//判断特殊字符,‘#’为当用户处于公共池中时与服务器断开连接,‘$’为准备建立私人聊天室连接池
public static int CheckHead(String words){
char head = words.charAt(0);
if(head == '#' ){
return 1;
}
else if (head == '$'){
return 2;
}
return 0;
}
//客户端与服务器友好的断开连接
public static void Finish(String name) throws IOException {
Socket socket = PublicConnect.get(name);
ServerSocket serverSocket = ConnectPort.get(name);
PublicConnect.remove(name);
ConnectPort.remove(name);
OutputStream outputStream = socket.getOutputStream();
String finish = "连接断开";
outputStream.write(finish.getBytes());//回复用户断开连接的请求
socket.close();
serverSocket.close();
if (modeflag == 2){
System.out.println(name + "断开与服务器的连接");
}
}
//公共池获取客户端输入流数据,配合轮询食用
public static int PublicReceive(Socket dataSocket,byte[] bytes) throws IOException {
InputStream inputStream = dataSocket.getInputStream();
try {
dataSocket.setSoTimeout(100); //设置输入流获取超时防止线程堵塞
int num = inputStream.read(bytes);
return num;
}
catch (Exception e){
return 0;
}
}
//私人池获取客户端输入流数据,配合轮询食用
public static int PrivateReceive(Socket dataSocket,byte[] bytes) throws IOException {
InputStream inputStream = dataSocket.getInputStream();
try {
dataSocket.setSoTimeout(100);
int num = inputStream.read(bytes);
return num;
}
catch (Exception e){
return 0;
}
}
//将指定客户端的Socket从公共池转移至指定私人池,建立私人池时使用
public static HashMap<String,Socket> AddPrivateConnectPool(String username,Socket dataSocket,HashMap<String,Socket> PrivateConnect){
PrivateConnect.put(username,dataSocket);
PublicConnect.remove(username);
PublicConnectSet = PublicConnect.entrySet();//更新公共池连接集合
return PrivateConnect;
}
//将指定客户端的Socket从私人池转移回公共池,退出私人池时使用
public static HashMap<String,Socket> Exit(String name,Socket socket,HashMap<String,Socket> PrivateConnect){
PublicConnect.put(name,socket);
PrivateConnect.remove(name);
PublicConnectSet = PublicConnect.entrySet();
try {
OutputStream outputStream = socket.getOutputStream();
String str = "您离开了私人聊天室,已返回公共聊天室";
outputStream.write(str.getBytes());
} catch (IOException e) {
}
if (modeflag == 2){
System.out.println(name + "离开了一个私人聊天室");
}
Set<Map.Entry<String,Socket>> PrivateConnectSet = PrivateConnect.entrySet();//建立一个临时私人池连接集合,用于向私人池内其他用户广播该用户退出信息
for (Map.Entry<String,Socket> entry : PrivateConnectSet){
try {
OutputStream outputStream1 = entry.getValue().getOutputStream();
String str1 = name + "离开了私人聊天室";
outputStream1.write(str1.getBytes());
} catch (IOException e) {
}
}
return PrivateConnect;
}
//8080端口监听处理线程(常开)
static class GetConnectionThread extends Thread{
@Override
public void run() {
if (modeflag == 2){
System.out.println("创建新连接线程启动");
}
while (true){
try {
BuildNewConnect(listenSocket);
PublicConnectSet = PublicConnect.entrySet();//更新集合
} catch (Exception e) {
if (modeflag == 2){
System.out.println("发生一次连接失败");
}
}
}
}
}
//公共池广播线程(常开)
static class PublicBroadcastThread extends Thread{
@Override
public void run() {
if (modeflag == 2){
System.out.println("公共聊天室广播线程启动");
}
byte[] bytes = new byte[1024];
while (true){
privateflag = 0;
try {
for (Map.Entry<String,Socket> entry : PublicConnectSet){ //轮询用户
Socket dataSocket = entry.getValue();
String NowUser = entry.getKey();
int num = PublicReceive(dataSocket,bytes);
if(num == 0){
continue;
}
else {
String words = new String(bytes,0,num);
if (modeflag == 2){
System.out.println(NowUser + ":" + words);
}
if(CheckHead(words) == 1){
Finish(NowUser);
}
else if (CheckHead(words) == 2){
new BuildPrivateConnectThread(NowUser,dataSocket).start();
while (true){
Thread.sleep(100);//必须要睡一小会,不然线程会被强制关闭?
if(privateflag == 1){
break;
}
}
}
else {
for (Map.Entry<String,Socket> entry1 : PublicConnectSet){ //向其他用户进行广播
if(entry1.getKey().equals(NowUser)){
continue;
}
else {
Socket othersSocket = entry1.getValue();
String str = NowUser + ":" + words;
OutputStream outputStream = othersSocket.getOutputStream();
outputStream.write(str.getBytes());
}
}
}
}
}
}
catch (Exception e){
}
}
}
}
//私人池创建线程,一次性使用
static class BuildPrivateConnectThread extends Thread {
private String NowUser;
private Socket dataSocket;
public BuildPrivateConnectThread(String NowUser,Socket dataSocket){
this.NowUser = NowUser;
this.dataSocket = dataSocket;
}
@Override
public void run() {
if (modeflag == 2){
System.out.println("私人聊天室创建线程启动");
}
byte[] bytes = new byte[1024];
try {
HashMap<String, Socket> PrivateConnect = new HashMap<>();
Set<Map.Entry<String, Socket>> PrivateConnectSet = null;
PrivateConnect = AddPrivateConnectPool(NowUser, dataSocket, PrivateConnect);//给每个私人聊天室建立一个单独的池
PrivateConnectSet = PrivateConnect.entrySet();
OutputStream outputStream = dataSocket.getOutputStream();
InputStream inputStream = dataSocket.getInputStream();
String str1 = "准备创建" + NowUser + "的私人聊天室";
String str2 = "请输入要邀请加入私人聊天室的成员名字,输入-1结束";
outputStream.write(str1.getBytes());
outputStream.write(str2.getBytes());
while (true) { //输入公共池存在的用户名,拉该用户入私人池,-1结束
try {
int num1 = inputStream.read(bytes);
String name = new String(bytes, 0, num1);
if ("-1".equals(name)) {
break;
} else {
for (Map.Entry<String, Socket> entry1 : PublicConnectSet) {
if (name.equals(entry1.getKey())) {
String NewUser = entry1.getKey();
Socket socket = entry1.getValue();
PrivateConnect = AddPrivateConnectPool(NewUser, socket, PrivateConnect);
PrivateConnectSet = PrivateConnect.entrySet();
OutputStream outputStream1 = socket.getOutputStream();
String str = "您被拉入" + NowUser + "的私人聊天室";
outputStream1.write(str.getBytes());
}
}
}
}
catch (Exception e){
}
}
PrivateBroadcastThread PrivateBroadcast = new PrivateBroadcastThread(PrivateConnect, PrivateConnectSet);
PrivateBroadcast.start();
String success = "私人聊天室创建成功";
outputStream.write(success.getBytes());
privateflag = 1;
} catch (Exception e) {
}
if (modeflag == 2){
System.out.println("私人聊天室创建线程关闭");
}
}
}
//私人池广播线程,一个私人池一个线程,私人池为空时自动关闭对应线程
static class PrivateBroadcastThread extends Thread{
private HashMap<String,Socket> PrivateConnect;
private Set<Map.Entry<String,Socket>> PrivateConnectSet;
public PrivateBroadcastThread(HashMap<String,Socket> PrivateConnect,Set<Map.Entry<String,Socket>> PrivateConnectSet) {
this.PrivateConnect = PrivateConnect;
this.PrivateConnectSet = PrivateConnectSet;
}
@Override
public void run() {
if (modeflag == 2){
System.out.println("一个私人聊天室广播线程启动");
}
byte[] bytes = new byte[1024];
while (true){
try {
for (Map.Entry<String,Socket> entry : PrivateConnectSet){
Socket dataSocket = entry.getValue();
String NowUser = entry.getKey();
int num = PrivateReceive(dataSocket,bytes);
if(num == 0){
continue;
}
else {
String words = new String(bytes,0,num);
if (modeflag == 2){
System.out.println("$" + NowUser + ":" + words);
}
if("%".equals(words)){ //输入%离开私人聊天室
PrivateConnect = Exit(NowUser, dataSocket, PrivateConnect);
PrivateConnectSet = PrivateConnect.entrySet();
}
else {
for (Map.Entry<String,Socket> entry1 : PrivateConnectSet){
if(entry1.getKey().equals(NowUser)){
continue;
}
else {
Socket othersSocket = entry1.getValue();
String str = "$" + NowUser + ":" + words;
OutputStream outputStream = othersSocket.getOutputStream();
outputStream.write(str.getBytes());
}
}
}
}
}
}
catch (Exception e){
}
if (PrivateConnect.isEmpty()){
if (modeflag == 2){
System.out.println("一个私人聊天室无人连接,聊天室线程关闭");
}
break;
}
}
}
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//客户端,如需建立多个客户端只需进行复制黏贴,然后修改类名和连接用户名即可
public class Customer1 {
public static void main(String[] args) throws IOException {
//主线程,负责获取连接,启动接收线程和负责发送数据
Scanner scanner = new Scanner(System.in);
Socket socket = getConnect(8080);
//接收数据线程
Thread receiveThread = new Thread(){
@Override
public void run() {
byte[] bytes = new byte[1024];
while (true){
try {
InputStream inputStream = socket.getInputStream();
int num = inputStream.read(bytes);
String str = new String(bytes,0,num);
System.out.println(str);
}
catch (Exception e){
}
}
}
};
receiveThread.start();
while (true){
String say = scanner.nextLine();
if("#".equals(say)){
Finish(socket,receiveThread);
return;
}
else {
OutputStream outputStream = socket.getOutputStream();
outputStream.write(say.getBytes());
System.out.println("我:" + say);
}
}
}
//与服务器获取连接(TCP)
public static Socket getConnect(int connectport) throws IOException {
Socket TCPsocket = new Socket("127.0.0.1",connectport);
byte[] bytes = new byte[1024];
InputStream inputStream = TCPsocket.getInputStream();
OutputStream outputStream = TCPsocket.getOutputStream();
String username = "Customer1"; //连接用户名
outputStream.write(username.getBytes());
int num = inputStream.read(bytes);
int port = Integer.parseInt(new String(bytes,0,num));//获取新端口号
Socket datasocket = new Socket("127.0.0.1",port);
TCPsocket.close();
return datasocket;
}
//与服务器友好断开连接并关闭客户端
public static void Finish(Socket socket,Thread thread) throws IOException {
OutputStream outputStream = socket.getOutputStream();
outputStream.write("#".getBytes());
try {
Thread.sleep(2000);
}
catch (Exception e){
}
thread.stop();
socket.close();
}
}