项目:一个公共聊天室功能的实现,实现了登录聊天,保存聊天记录等功能。
一、实现代码
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