java基于socket公共聊天室的实现

项目:一个公共聊天室功能的实现,实现了登录聊天,保存聊天记录等功能。

一、实现代码

1.客户端

ChatClient.java

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;
import java.util.StringTokenizer;

public class ChatClient {
	// 创建一个Socket
	private static Socket client = null;
	// 定义输入流变量
	private static DataInputStream di = null;
	// 定义输出流变量
	private static DataOutputStream dos = null;
	// 创建scanner对象接收数据
	Scanner sc = new Scanner(System.in);
	// 定义一个String变量保存用户名
	static String name;
	// 定义一个布尔值来判断是否循环接受用户名
	boolean falg = true;

	public static void main(String args[]) {
		// 创建对象
		ChatClient cc = new ChatClient();
		// 创建客户端的socket对象
		client = new Socket();
		// 调用方法
		cc.connect();
		// 调用方法
		cc.send();
	}

	// 连接
	public void connect() {

		// 创建客户端的socket对象
		client = new Socket();
		// 定义一个String接收IP地址
		String IP = null;
		// 定义一个int端口号
		int port = 0;
		System.out.println("***!!Welcome!!***");
		try {
			System.out.println("输入服务器的默认地址是0或127.0.0.1");
			// 接收一个IP保存到string对象中
			IP = sc.nextLine();
			// 匹配接收到的IP,接收到后将IP赋值为默认
			if (IP.equalsIgnoreCase("0")) {
				IP = "";
			}
			// 设置端口号
			port = 8000;
		} catch (Exception e) {
		}
		try {
			// 从给定的主机名得到ip存入inetaddress对象中
			InetAddress address = InetAddress.getByName(IP);
			// 根据得到的ip和端口号创建套接字地址
			InetSocketAddress socketaddress = new InetSocketAddress(address,
					port);
			// 将客户端的套接字链接到服务器
			try {
				// 连接服务器与客户端
				client.connect(socketaddress);
				// 判断是否有连接
				if (client.isConnected()) {
					// 调用方法
					runn();
				} else {
					client.connect(socketaddress);
					// 调用方法
					connect();
				}
			} catch (SocketException e) {
				System.out.println("不能连接到服务器,请重新输入");
				// 调用connect()重新连接
				connect();
			}
		} catch (Exception e) {
			System.out.println("不能连接到服务器,请重新输入");
			// 调用connect()重新连接
			connect();
		}
	}

	public void runn() {
		try {
			// 定义read对象
			ClientThread read = null;
			// 创建read对象
			read = new ClientThread();
			// 创建readdata线程对象
			Thread readData = new Thread(read);
			// 封装一个DataInputStream对象得到输入流
			di = new DataInputStream(client.getInputStream());
			// 封装一个DataOutputStream对象得到输出流
			dos = new DataOutputStream(client.getOutputStream());
			// 接受用户名
			while (falg) {
				System.out.println("请输入用户名:");
				name = sc.next();
				System.out.println(name + "上线了");
				System.out.println("欢迎进入聊天室,需要帮助请输入/A");
				dos.writeUTF(name);
				dos.flush();
				read.setDataInputStream(di);
				// 启动线程
				readData.start();
				// 改变flag中断循环
				falg = false;
			}
		} catch (IOException e) {

		}
	}

	// 写入信息
	@SuppressWarnings("deprecation")
	public void send() {
		// 循环接收发送的消息
		System.out.println("请输入内容:");
		while (sc.hasNext()) {
			String mess;
			mess = sc.nextLine();
			if (mess.equalsIgnoreCase("/0")) {
				System.exit(0);
			} else if (mess.equalsIgnoreCase("/D")) {
				try {
					File file = new File("qq.txt");
					di = new DataInputStream(new FileInputStream(file));
					String info = null;
					System.out.println("1 微笑;2 哭泣;3 疑问;4 加油;5 喔耶~;");
					System.out.println("6 幸福;7 好冷;8 刺眼;9 昏倒;10 幸福;");
					System.out.println("请选择表情:");
					String age = sc.next();
					while ((info = di.readLine()) != null) {
						StringTokenizer stri = new StringTokenizer(info, ".");
						String inn = stri.nextToken();
						String in = stri.nextToken();
						// System.out.println(in);

						if (age.equals(inn)) {
							dos.writeUTF(in);
							break;
						}
					}
				} catch (Exception e) {

				}
			}
			if (mess.equals("/D")) {
				System.out.println("请选择表情:");
			} else {
				select(mess);
			}
		}
	}

	// 将消息发送给服务器
	public void select(String mess) {
		// 判断输入的信息
		if (mess.equalsIgnoreCase("/A")) {
			// 匹配上调用helpList()方法
			helpList();
		} else if (mess.equalsIgnoreCase("/C")) {
			try {
				System.out.println("请输入你要查看的聊天记录的名字");
				String str1 = sc.next();
				File file = new File(str1 + ".txt");
				BufferedReader bf = new BufferedReader(new FileReader(file));
				String str = null;
				while ((str = bf.readLine()) != null) {
					System.out.println(str);
				}
				bf.close();
			} catch (IOException e) {

			}

		} else {
			try {
				// 将消息发送给服务器
				dos.writeUTF(mess);
				// 清空输出流
				dos.flush();
			} catch (IOException e) {

			}
		}
	}

	public void helpList() {
		System.out.println("提示:进入聊天室,默认公聊!!");
		System.out.println("/B 用户在线列表,用户/信息 私聊,/C 查看聊天记录,/D 发送表情,/0 退出系统");
	}
}

ClientThread.java

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class ClientThread implements Runnable {
	// 定义一个DataInputStream变量
	DataInputStream di;
	// 定义一个string对象接收服务器发送的消息
	String str;
	boolean ff=true;

	public void setDataInputStream(DataInputStream di) {
		this.di = di;
	}

	public void run() {
		while (true) {
			try {
				// 从流中读取信息
				str = di.readUTF();
				// 调用writeFile将信息写入文件中
				writeFile(str);
				// 显示信息
				System.out.println(str);
			} catch (IOException e) {
				// 服务器断开后客户端显示提示信息
				System.out.println("服务断开~~~~~~~");
				// 终止客户端
				System.exit(0);
			}
		}
	}

	public void writeFile(String str2) {
		try {
			// 创建一个文件
			File file = new File(ChatClient.name + ".txt");
			FileWriter fw = new FileWriter(file, true);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(str2);
			// 写入一个行分隔符
			bw.newLine();
			// 关闭缓冲流
			bw.close();
			// 关闭字符输出流
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
 

2.服务端

ChatServer.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {
	// 定义一个serversocket对象
	private static ServerSocket server = null;
	// 定义一个socket对象
	private static Socket client = null;
	// 定义一个变量用来保存客户端的用户名
	private static String name;
	// 定义一个布尔值变量
	private static boolean falg = true;
	// 定义输入流变量
	private static DataInputStream di = null;
	// 定义输出流变量
	private static DataOutputStream dos = null;

	public static void main(String args[]) {
	// 创建ServerThread对象
	ServerThread st = new ServerThread();
	while (true) {
		try {
			// 创建serversocket对象指定端口号为8000
			server = new ServerSocket(8000);
		} catch (IOException e) {
			System.out.println("正在监听!!");
		}
		try {
			System.out.println("等待客户端连接....");
			// 将客户端的套接字与服务器的套接字连接起来
			client = server.accept();
			System.out.println("连接成功!!");
			// 将服务器的输入流封装到DataInputStream中
			di = new DataInputStream(client.getInputStream());
			// 将服务器的输出流封装到DataInputStream中
			dos = new DataOutputStream(client.getOutputStream());
			// 从流中读取用户名
			while (falg) {
				name = di.readUTF();
				if (st.checkp(name)) {
					System.out.println("客户的地址:" + client.getInetAddress()+ "\t" + name + ":进入聊天室");
					// 将falg变为false
					falg = false;
				} else {
					// 清空输出流
					dos.flush();
				}
			}
		} catch (IOException e) {
			System.out.println("正在等待客户端呼叫......");
		}

		// 判断是否有客户端连接到服务器
		if (client.isConnected()) {
			// 创建服务器端的收发信息线程对象
			ServerThread sth = new ServerThread(client, name);
			Thread th = new Thread(sth);
			// 启动线程
			th.start();
			falg = true;
			}
		}
	}
}

ServerThread.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;

public class ServerThread implements Runnable {
	// 定义一个socket变量
	Socket client = null;
	// 定义一个Datainputstream变量
	DataInputStream di = null;
	// 定义一个Dataoutputstream变量
	DataOutputStream dos = null;
	// 定义一个变量保存连接当前线程的用户名
	String name = null;
	// 创建一个hashtable对象用来保存所有的为客户端开辟的线程对象
	static Hashtable<String, ServerThread> clientlist = new Hashtable<String, ServerThread>();

	public ServerThread() {

	}

	public ServerThread(Socket client, String name) {
		try {
			// 将传入的client赋值给成员变量的client
			this.client = client;
			// 将传入的name赋值给成员变量的name
			this.name = name;
			// 将服务器的输出流封装到DataInputStream中
			di = new DataInputStream(client.getInputStream());
			// 将服务器的输出流封装到DataOutputStream中
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {

		}
	}

	public void run() {
		try {
			// 添加当前对象到hashtable
			clientlist.put(name, this);
			// 发送新用户进入的消息给所有客户端
			sendallClient(name + "进入聊天室");

			while (true) {

				// 定义一个string对象接受从流中读取到的信息
				String mess = di.readUTF();
				// 创建一个stringtokenizer对象分析接收到的消息
				StringTokenizer str = new StringTokenizer(name, "/");
				// 判断截取到的信息有没分隔符
				// 如果有分隔符则判断为私聊发送信息
				if (str.countTokens() == 2) {
					// 得到要发送私聊信息用户的姓名
					String nameid = str.nextToken();
					// 得到要发送的私聊信息
					String message = str.nextToken();
					// 调用发sendclient送私聊信息
					sendClient(nameid, message);

					// 没有分隔符或者有多个分隔符是信息默认为公聊发送
				} else if (mess.equalsIgnoreCase("/B")) {
					// 匹配到调用getlist方法
					getList();
					// 判断信息是否与-change匹配
				} else if (mess.equalsIgnoreCase("/0")) {
					System.out.println("name" + "退出聊天室");
					break;
				} else if (str.countTokens() == 1 || str.countTokens() >= 3) {
					// 调用sendallclient发送公聊信息
					sendallClient(name + "说:" + mess);
				}
			}
			client.close();
		} catch (Exception e) {

		} finally {
			// 清除客户端信息
			clientlist.remove(name);
			File file = new File(name + ".txt");
			// 文件删除
			file.delete();
			// 有人退出时,给所有人发送退出信息
			sendallClient(name + "退出聊天室");
			System.out.println(getDate() + " " + name + "退出聊天室");
		}
	}

	// 公聊
	public void sendallClient(String mess) {
		// 获得clientlist中值得ServerThread放入枚举集合中
		Enumeration<ServerThread> allclients = clientlist.elements();
		// 遍历所有客户
		while (allclients.hasMoreElements()) {
			// 枚举中还有元素是,返回此举的下一个元素
			ServerThread st = (ServerThread) allclients.nextElement();
			try {
				// 将信息写入流中
				st.dos.writeUTF(getDate() + "\t" + mess);
				// 刷新流
				st.dos.flush();
			} catch (IOException e) {
				Thread th = new Thread(st);
				// 产生异常中断此线程
				th.interrupt();
			}
		}
	}

	// 私聊
	public void sendClient(String name1, String mess) {
		ServerThread st = clientlist.get(name1);
		ServerThread st1 = clientlist.get(name);
		try {
			// 将要发送的信息保存到流中
			st.dos.writeUTF(getDate() + "\t" + name + "对你说:\t" + mess);
			// 刷新流
			st.dos.flush();

			// 把信息发给原客户端
			st1.dos.writeUTF(getDate() + "\t你对" + name1 + "说:\t" + mess);
			// 刷新流
			st1.dos.flush();
		} catch (IOException e) {

			System.out.println("你发送的信息有误,请重新发送!");
			//sendClient(name1, mess);
		}
	}

	public boolean checkp(String str) {
		boolean flag = true;
		// 得到所有的用户名
		Enumeration<String> checkname = clientlist.keys();
		// 循环检查用户名
		while (checkname.hasMoreElements()) {
			// 枚举中还有元素是,返回此举的下一个元素
			String sn = (String) checkname.nextElement();
			// 判断用户名是否重复,重复的话返回false
			if (str.equalsIgnoreCase(sn)) {
				// 返回false
				flag = false;
				// 判断用户名是否为空
			} else if (str.equalsIgnoreCase("")) {
				// 返回false
				flag = false;
			}
		}
		return flag;
	}

	// 关闭服务器
	public void guanBi() {
		if (!(client == null)) {
			try {
				client.close();
				System.out.println("客户端退出此系统");
			} catch (IOException e) {

			}
		}
	}

	// 显示用户在线
	public void getList() {
		// 获得所有键的枚举
		Enumeration<String> checkname = clientlist.keys();
		// 通过用户名获得线程对象
		ServerThread st = clientlist.get(name);
		try {
			// 写入信息
			st.dos.writeUTF("在线用户列表");
			// 写入用户在线人数
			st.dos.writeUTF(clientlist.size() + ":人在线");
		} catch (IOException e1) {

			e1.printStackTrace();
		}
		// 便利枚举
		while (checkname.hasMoreElements()) {
			// 枚举中还有元素是,返回此举的下一个元素
			String sn = (String) checkname.nextElement();
			try {
				// 将用户信息写入流中
				st.dos.writeUTF("用户名称:" + sn);
				// 清空输出流
				st.dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	// 时间
	public String getDate() {
		// 获得data对象
		Date nowTime = new Date();
		// 创建格式化参数
		String pattern = "HH:mm:ss";
		// 创建SimpleDateFormat对象
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		// 定义一个变量接收时间
		String timePattern = sdf.format(nowTime);
		// 返回当前时间
		return timePattern;
	}
}

二、补充

1.制作.bat执行文件

创建client.bat和server.bat,编辑.bat文件为 java -jar client.jar 和 java -jar server.jar

上一篇:Android 基于Socket的聊天室(一)


下一篇:【VS开发】【Live555-rtsp】RTSP服务器实例live555源代码分析