首先的话,这个代码主要是我很久以前写的,然后当时还有很多地方没有理解,现在再来看看这份代码,实在是觉得丑陋不堪,想改,但是是真的改都不好改了…
所以,写代码,规范真的很重要。

实现的功能:

  1. 用户私聊
  2. 群聊功能:进群退群,群发消息,查看群聊
  3. 查看自己的消息记录
  4. 通过文件流,设置敏感词汇过滤(这里还用到了字典树…)不过我还有点不熟练…
  5. 离线,退出登录

不足:
emmm,其实说到不足的地方实在是太多了。
首先功能并没有完全完善,尤其是群聊的功能(但是我觉得后面的功能实现了也意义不大了)
然后,写代码并不规范。
也没有用到Java面向对象的知识,只存放用户的昵称,没有设置密码什么的
还要设置个,当用户上线之后,再来发送缓存之中的数据。
数据没有保存起来,关闭程序之后,数据消失了…
最开始的时候,我还不怎么会使用集合,后面发现在集合中放List(其实就是对象嘛),可以存很多东西的…
总而言之就是,还是要多写代码的,同时要多学习知识啊!!!

开始上代码!

目录结构


我这里的话,把我这四个类都放在test1包下。

实在是,这里用到了很多if else…然后,我这里想调用对应的方法语句都是要先输入对应的中文前缀的,很麻烦…建议大家使用循环,然后每次提示用户输入对应的指令。

TCPServer

首先就是要先启动服务端,如果没有启动的话是会报错的!!!

package test1;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TCPServer {public static void main(String[] args) {TCPServer server = new TCPServer();server.start();}/*** 创建线程池来管理客户端的连接线程* 避免系统资源过度浪费*/private ServerSocket serverSocket;private ExecutorService exec;/*** 存放用户昵称,打印流*/private Map<String, PrintWriter> storeInfo;/*** 存放客户端之间私聊的信息*/private Map<String, LinkedList<String>> storeMessages;/*** 用户没有上线的消息【我这里没有实现】*/private Map<String, String> waitMessages;/*** 群 K:群名称,Value:人员的昵称集合*/Map<String, LinkedList<String>> aGroup = new LinkedHashMap<>();public TCPServer() {//构造器try {serverSocket = new ServerSocket(9999);storeInfo = new ConcurrentHashMap<String, PrintWriter>();exec = Executors.newCachedThreadPool();//创建线程池storeMessages = new ConcurrentHashMap<String, LinkedList<String>>();} catch (IOException e) {e.printStackTrace();}}public void start() {try {while (true) {System.out.println("服务端等待客户端连接... ... ");Socket socket = serverSocket.accept();//出现一个客户才会accept()System.out.println("客户端:“" + socket.getInetAddress().getHostAddress() + "”连接成功! ");//启动一个线程,由线程来处理客户端的请求,这样可以再次监听下一个客户端的连接//每次都是一个服务端Socket跟一个客户端绑定。同时服务端开启监听客户端的请求输入流exec.execute(new ListenerClient(socket)); //通过线程池来分配线程      监听客户机    出现一个客户,就新建一个线程}} catch (Exception e) {e.printStackTrace();}}/*** 创建内部类来获取昵称* 监听客户端* 该线程体用来处理给定的某一个客户端的消息,循环接收客户端发送 的每一个字符串,并输出到控制台*/class ListenerClient implements Runnable {private Socket socket;private String name;public ListenerClient(Socket socket) {this.socket = socket;}/*** 管理昵称的方法*/private String getName() throws Exception {//服务端的输入流读取客户端发送来的昵称输出流BufferedReader bReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));//服务端将昵称验证结果通过自身的输出流发送给客户端PrintWriter ipw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);//读取客户端发来的昵称while (true) {String nameString = bReader.readLine();if ((nameString.trim().length() == 0) || storeInfo.containsKey(nameString)) {ipw.println("FAIL");//发送给客户端} else {ipw.println("OK");return nameString;}}}/*** 监听方法* 通过客户端的Socket获取客户端的输出流,用来将消息发送给客户端*/@Overridepublic void run() {try {PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);name = getName();putIn(name, pw, storeInfo);//将客户昵称和服务端所说的内容存入共享集合HashMap-storeInfo中Thread.sleep(100);// 服务端通知所有客户端,某用户上线(是群发的)sendToAll("[系统通知] “" + name + "”已上线");System.out.println("[系统通知] “" + name + "”已上线");/* 读取客户端* 通过客户端的Socket获取输入流读取客户端发送来的信息*/BufferedReader bReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));String msgString = null;//用msgString来获取客户端的输入while ((msgString = bReader.readLine()) != null) {LinkedList<String> msgList = storeMessages.get(name);if (msgList == null) {msgList = new LinkedList<String>();}// 检验是否为私聊(格式:@昵称:内容)if (msgString.startsWith("@")) {int index = msgString.indexOf(":");//@deng:xxx(冒号后面为内容)if (index == -1) {index = msgString.indexOf(":");}if (index >= 0) {String theName = msgString.substring(1, index);//获取昵称String info = msgString.substring(index + 1);//获取发送的内容if (isBlank(info)) {pw.println("[系统提示]:给好友发送的消息不能为空哦!");}info = "“" + name + "”:" + info;//将私聊信息发送出去boolean b = sendToSomeone(theName, isIllegal(info));if (!b) {//返回给原来的用户:消息发送失败,找不到人pw.println("[系统提示]:未找到指定用户“" + theName + "“,消息发送失败");}msgList.add(msgString);storeMessages.put(name, msgList);continue;}} else if (msgString.startsWith("新建群:") || msgString.startsWith("新建群:")) {creatGroup(msgString, name);continue;} else if (msgString.contains("群邀请:") || msgString.contains("群邀请:")) {groupInvite(msgString, name);continue;} else if (msgString.startsWith("退群:") || msgString.startsWith("退群:")) {String groupName = msgString.substring(3);exitGroup(groupName, name);continue;} else if (msgString.startsWith("我想进群:") || msgString.startsWith("我想进群:") ) {String groupName = msgString.substring(5);acceptGroup(groupName, name);continue;} else if (msgString.contains("查看我加入的群聊")) {pw.println("我加入的群聊有"+showMyGroups(name));continue;} else if ("查看消息记录".equals(msgString)) {printOnlineUser(name);printAllMessages(name);continue;} else if ("撤回最近一条消息".equals(msgString)) {withdraw(name);continue;} else if (msgString.startsWith("群发给")) {//在群里发信息:群发给XXX:。。。1.判断是否在群XXX里   2.群发内容int index = msgString.indexOf(':');if (index == -1) {index = msgString.indexOf(":");}String theGroupName = msgString.substring(3, index);//群聊名称String theMsg = msgString.substring(index + 1);//内容sendToGroup(theGroupName, isIllegal(theMsg), name);continue;}if ("exit".equals(msgString)) {//手动退出程序remove(name);sendToAll("[系统通知] “" + name + "”已经手动下线了。");// 通知所有客户端,某某客户已经下线}//否则:(自己乱发消息) 遍历所有输出流,将该客户端发送的信息转发给所有客户端System.out.println(name + ":" + isIllegal(msgString));sendToSomeone(name, isIllegal(msgString));msgList.add(msgString);storeMessages.put(name, msgList);}} catch (Exception e) {// e.printStackTrace();} finally {remove(name);sendToAll("[系统通知] “" + name + "”已经下线了。");// 通知所有客户端,某某客户已经下线if (socket != null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}}private boolean isBlank(String msg) {return msg == null || "".equals(msg.trim());}private void putIn(String name, PrintWriter value, Map<String, PrintWriter> store) {synchronized (this) {store.put(name, value);}}/*** 将给定的输出流从共享集合中删除*/private synchronized void remove(String key) {storeInfo.remove(key);System.out.println("系统当前在线人数为:" + storeInfo.size());}/*** 将给定的消息转发给所有客户端(群发)*/private synchronized void sendToAll(String message) {for (PrintWriter everyone : storeInfo.values()) {everyone.println(message);}}/*** 群发信息** @param groupName 群聊名称* @param message   群发的内容* @param username  发信息的人*/private synchronized void sendToGroup(String groupName, String message, String username) {LinkedList<String> nickNameList = aGroup.get(groupName);if (nickNameList == null) {sendToSomeone(username, "未找到指定的群:" + groupName);} else {for (String name : nickNameList) {sendToSomeone(name, "【"+groupName + "】群的“" + username + "”说" + message);}}}/*** 将给定的消息转发给私聊的客户端*/private synchronized boolean sendToSomeone(String name, String message) {//每一个名字对应一个只给它发送信息的PrintWriterPrintWriter pw = storeInfo.get(name); //将对应客户端的聊天信息取出作为私聊内容发送出去(相当于只发给指定的name的)if (pw != null) {pw.println(message);return true;}return false;}private static final SensitiveFilter sensitiveFilter = new SensitiveFilter();/*** 检测从服务端读入的信息是否合法-->读取 不含敏感信息* 否侧把敏感词汇进行过滤*/private synchronized String isIllegal(String message) {String msg = sensitiveFilter.filter(message);if (msg == null) {return "[系统提示]:消息为空,发送失败了";}return msg;}private synchronized void creatGroup(String msgString, String hostname) {String groupName = msgString.substring(4);//1.建立Map,添加群主及群聊名称LinkedList<String> nickNameList = new LinkedList<>();nickNameList.add(hostname);aGroup.put(groupName, nickNameList);//2.发送信息给客户端,问要邀请谁sendToSomeone(hostname, "你的【" + groupName + "】群聊创建成功!您可以邀请人了");System.out.println("“" + hostname + "”新建了【" + groupName + "】群聊");//3.群主发信息邀请其他成员}private synchronized void groupInvite(String msgString, String hostname) {//"xx群邀请:昵称"int locate = msgString.indexOf("群邀请:");String subString = msgString.substring(locate + 4);//获取昵称String groupname = msgString.substring(0, locate);//群聊名称//3.2检查是哪位成员   (找key)Set<String> names = storeInfo.keySet();for (String needName : names) {if (needName.equals(subString)) {sendToSomeone(needName, "群主“" + hostname + "”邀请你加入【" + groupname + "】 群聊");sendToSomeone(hostname, "群邀请信息发送成功,等待“" + needName + "”回应");return;}}//没有找到成员sendToSomeone(hostname, "对不起,没有找到“ " + subString + "”成员,请重试。");}/*** 例如:我想进群:(群)** @param groupName 进群语句* @param aName     发送语句人名称* @return true说明包含了进群 or 退群指令*/private synchronized void acceptGroup(String groupName, String aName) {//遍历已知的群聊String hostname = null;LinkedList<String> nickList = aGroup.get(groupName);if (nickList == null) {sendToSomeone(aName, "进群失败,您输入的群名称无效!");} else {//进群,并通知群里所有的人nickList.add(aName);aGroup.put(groupName, nickList);for (String s : nickList) {sendToSomeone(s, "[系统消息]:刚刚“" + aName + "”已经加入群【" + groupName + "】啦");}}}/*** 用户退群*/private synchronized void exitGroup(String groupName, String aName) {LinkedList<String> nickNameList = aGroup.get(groupName);if (nickNameList == null) {sendToSomeone(aName, "退群失败,您输入的群名称无效!");} else {nickNameList.remove(aName);aGroup.put(groupName, nickNameList);sendToSomeone(aName, "您已经成功退群【"+groupName+"】了");sendToGroup(groupName, aName+"退群!", aName);}}/*** 显示我加入的群聊*/private synchronized List<String> showMyGroups(String aName) {List<String> listGroup = new ArrayList<>();for (String group : aGroup.keySet()) {LinkedList<String> nickNameList = aGroup.get(group);if (nickNameList.contains(aName)) {listGroup.add(group);}}return listGroup;}public synchronized void printOnlineUser(String myName) {Set<String> set = storeInfo.keySet();StringBuilder sb = new StringBuilder().append("所有在线用户:{");for (String obj : set) {sb.append(obj).append(",");}sb.deleteCharAt(sb.length() - 1);sb.append("}");sendToSomeone(myName, sb.toString());}/*** 打印我之前发过的所有消息*/public synchronized void printAllMessages(String name) {Set<String> set = storeMessages.keySet();String sb = "我之前发过的信息有:" + isIllegal(storeMessages.get(name).toString());sendToSomeone(name, sb);}/*** 消息的撤回  看需求,可以撤回最近的一条消息** @param name*/public synchronized void withdraw(String name) {LinkedList<String> list = storeMessages.get(name);if (list == null || list.isEmpty()){sendToSomeone(name, name + "现在还没有消息可以撤回");return;}String removeMsg = list.remove(list.size() - 1);storeMessages.put(name, list);sendToSomeone(name, name + "成功撤回最近的消息:" + removeMsg);System.out.println(name + "刚撤回了[" + removeMsg + "]消息");}public static void print() {System.out.println("|\t\t提示信息\t\t\t\t\t\t|");System.out.println("| 私聊:@xxx:。。。(内容)\t\t\t\t|");System.out.println("| 群聊:①新建群:xxx   ②群邀请:XX群邀请:昵称 |");System.out.println("| ③同意进群:我想进群:XXX    ④退群:退群:XXX    |");System.out.println("| 撤回最近一条消息 |");System.out.println("| 查看消息记录  ||  查看我加入的群聊 \t⑤群发信息:群发给XXX:。。。 |");System.out.println("|注:敏感词汇有:" + sensitiveFilter.getSensitiveWords().toString());System.out.println("| 退出登录用[exit]      |");System.out.println("+-----------------------------------------------+");}}

TCPClient

这里的客户端,一边进行不断通过Scanner的输入消息,另一边,通过listenerServer方法不断监听服务端发送过来的消息,并进行打印在控制台。

补充:如何同时启动多个客户端:

package test1;import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TCPClient {/*** 代表一个客户端*/static private Socket clientSocket;public static void main(String[] args) throws IOException {TCPServer.print();clientSocket = new Socket("localhost", 9999);TCPClient client = new TCPClient();client.start();}public void start() {try {System.out.println("客户机创建成功!---已自动登录本机IP:" + clientSocket.getInetAddress().getHostAddress());Scanner scanner = new Scanner(System.in);//设置昵称setName(scanner);// ① 接收  服务器端发送过来的信息的线程启动ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new listenerServer());//监听服务端的消息// ② 建立输出流,给服务端发送信息//字符流==>字节流;设立为true自动刷新缓冲区PrintWriter pw = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream(), StandardCharsets.UTF_8), true);while (true) {String msg = scanner.nextLine();if ("".equals(msg.trim())) {System.out.println("[系统提示]:自己发送的消息不能为空");continue;}//打印自己说的话pw.println(msg);if (msg.equals("exit")) {System.out.println("您已经退出客户端程序");clientSocket.shutdownOutput();}}} catch (Exception e) {e.printStackTrace();} finally {//关闭Socketif (clientSocket != null) {try {System.out.println("您已经下线");clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 设置昵称 并登录*/private void setName(Scanner scan) throws Exception {String name;//创建输出流PrintWriter pw = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"), true);//创建输入流BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));while (true) {System.out.println("请创建您的昵称(登录):");name = scan.nextLine();if ("".equals(name.trim())) {System.out.println("昵称不得为空");} else {pw.println(name);String pass = br.readLine();if (pass != null && (!"OK".equals(pass))) {     /*接收从br发送的输入中获取的信息       */System.out.println("昵称已经被占用,请重新输入:");} else {System.out.println("昵称“" + name + "”已设置成功,可以开始聊天了");break;}}}}/*** 监听(在客户做其他的事情的时候这个也一直 在运行,就可以接受服务端传过来的信息)* 循环读取  -服务端- 发送过来的信息并输出到客户端的控制台* 接收信息,来一句打印一句*/class listenerServer implements Runnable {@Overridepublic void run() {try {BufferedReader br = new BufferedReader(//读取clientSocket的输入流并打印new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8));String msgString;while ((msgString = br.readLine()) != null) {System.out.println(msgString);}} catch (Exception e) {System.out.println("出现异常");e.printStackTrace();}}}}

SensitiveFilter

实现过滤敏感词汇的功能:

注意这里的读取文件中的内容:

getResource方法: 查找具有给定名称的资源。 搜索与给定类相关联的资源的规则由类的定义类加载器实现。 此方法委托给此对象的类加载器。 如果此对象由引导类加载器加载,则该方法委托给ClassLoader.getSystemResource 。

String filePath = SensitiveFilter.class.getResource("sensitive-words.txt").getPath();

此时打印得到的filePath 就是文件的绝对路径了。然后再使用Reader进行读取:

BufferedReader reader = new BufferedReader(new FileReader(filePath));

package test1;import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;public class SensitiveFilter {/*** 替换符*/private static final String REPLACEMENT = "***";/*** 根节点*/private final TrieNode rootNode = new TrieNode();public static final ArrayList<String> sensitiveWordsList = new ArrayList<>();public SensitiveFilter() {try {String filePath = SensitiveFilter.class.getResource("sensitive-words.txt").getPath();BufferedReader reader = new BufferedReader(new FileReader(filePath));String keyword;while ((keyword = reader.readLine()) != null) {// 添加到前缀树this.addKeyword(keyword);sensitiveWordsList.add(keyword);}} catch (IOException e) {System.out.println("加载敏感词文件失败: " + e.getMessage());}}public ArrayList<String> getSensitiveWords() {return sensitiveWordsList;}/*** 将一个敏感词添加到前缀树中*/private void addKeyword(String keyword) {TrieNode tempNode = rootNode;for (int i = 0; i < keyword.length(); i++) {char c = keyword.charAt(i);TrieNode subNode = tempNode.getSubNode(c);if (subNode == null) {// 初始化子节点subNode = new TrieNode();tempNode.addSubNode(c, subNode);}// 指向子节点,进入下一轮循环tempNode = subNode;// 设置结束标识if (i == keyword.length() - 1) {tempNode.setKeywordEnd(true);}}}/*** 过滤敏感词** @param text 待过滤的文本* @return 过滤后的文本。如果源字符串是null或者为空,返回 null*/public String filter(String text) {if (text == null || "".equals(text.trim())) {return null;}// 指针1TrieNode tempNode = rootNode;// 指针2int begin = 0;// 指针3int position = 0;// 结果StringBuilder sb = new StringBuilder();while (position < text.length()) {char c = text.charAt(position);// 跳过符号if (isSymbol(c)) {// 若指针1处于根节点,将此符号计入结果,让指针2向下走一步if (tempNode == rootNode) {sb.append(c);begin++;}// 无论符号在开头或中间,指针3都向下走一步position++;continue;}// 检查下级节点tempNode = tempNode.getSubNode(c);if (tempNode == null) {// 以begin开头的字符串不是敏感词sb.append(text.charAt(begin));// 进入下一个位置position = ++begin;// 重新指向根节点tempNode = rootNode;} else if (tempNode.isKeywordEnd()) {// 发现敏感词,将begin~position字符串替换掉sb.append(REPLACEMENT);// 进入下一个位置begin = ++position;// 重新指向根节点tempNode = rootNode;} else {// 检查下一个字符position++;}}// 将最后一批字符计入结果sb.append(text.substring(begin));return sb.toString();}/*** 判断是否为符号*/private boolean isSymbol(Character c) {// 0x2E80~0x9FFF 是东亚文字范围return !isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);}private boolean isAsciiAlphanumeric(Character ch) {return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');}/*** 前缀树*/private class TrieNode {/*** 关键词结束标识*/private boolean isKeywordEnd = false;/*** 孩子节点(key是下级字符,value是下级节点)*/private Map<Character, TrieNode> subNodes = new HashMap<>();public boolean isKeywordEnd() {return isKeywordEnd;}public void setKeywordEnd(boolean keywordEnd) {isKeywordEnd = keywordEnd;}/*** 添加子节点*/public void addSubNode(Character c, TrieNode node) {subNodes.put(c, node);}// 获取子节点public TrieNode getSubNode(Character c) {return subNodes.get(c);}}public static void main(String[] args) throws Exception {String msg = "赌博交易违法啊 啊吸毒 啊啊吸毒开票嫖娼都是敏感词汇!!!";SensitiveFilter sensitiveFilter = new SensitiveFilter();String filter = sensitiveFilter.filter(msg);System.out.println(msg);System.out.println(filter);}
}

sensitive-words.txt

这个就是存放的敏感词汇了。
比如说我的:注意SensitiveFilter类会一行一行地读取这里面的敏感词!

拓展

最后的话,我还想到了使用springboot的websocket(实现页面的聊天以及消息推送),以前我也看过很多遍websocket的代码以及博客吧,但是吧,我每次懒,然后居然没有去自己去实现一遍啊啊啊。

想想就觉得傻。所以一定一定要去亲自做一遍,不然你永远也不知道自己会不会。
后面还有一个 是基于NIO的socket通信,我也想后面学习一下(❁´◡`❁)

Java基于Socket实现聊天、群聊、敏感词汇过滤功能相关推荐

  1. C语言基于socket的网络群聊室

    服务端: #include <pthread.h> #include<stdio.h> #include<WinSock2.h>//调用套接字的头函数  #incl ...

  2. socket java 客户端_Java基于socket实现的客户端和服务端通信功能完整实例

    本文实例讲述了Java基于socket实现的客户端和服务端通信功能.分享给大家供大家参考,具体如下: 以下代码参考马士兵的聊天项目,先运行ChatServer.java实现端口监听,然后再运行Chat ...

  3. 基于JavaSwing开发聊天室(QQ聊天 群聊)系统+论文+PPT 大作业 毕业设计项目源码

    基于JavaSwing开发聊天室(QQ聊天 群聊)系统+论文+PPT(毕业设计/大作业) 开发环境: Windows操作系统 开发工具: Eclipse/Myeclipse+Jdk 演示视频: jav ...

  4. Android 基于Socket的聊天室

    原文地址为: Android 基于Socket的聊天室 Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路, ...

  5. 2Python全栈之路系列之基于socket实现聊天机器人

    Python全栈之路系列之基于socket实现聊天机器人 通过socket实现局域网内的聊天工具. service.py文件如下: #!/usr/bin/env python # _*_ coding ...

  6. Java基于socket服务实现UDP协议的方法

    转载自 Java基于socket服务实现UDP协议的方法 这篇文章主要介绍了Java基于socket服务实现UDP协议的方法,通过两个简单实例分析了java通过socket实现UDP发送与接收的技巧, ...

  7. IM多人聊天-群聊头像合成方法

    IM多人聊天群聊头像合成方法 群聊中将多个成员的头像合成为群聊的头像: ViewController.m // // ViewController.m // imageMerge // // Crea ...

  8. 【python】基于Socket的聊天室Python开发

    基于Socket的聊天室Python开发 一.Socket简述 二.创建服务端Server 2.1 创建服务端初始化 2.2 监听客户端连接 2.3 处理客户端消息 三.创建客户端Client 3.1 ...

  9. 基于socket的聊天室实现原理

    基于socket的聊天室,目前还比较少见,国内比较知名的有网易和碧海银沙聊天室.这种聊天室的特点很明显,不象CGI聊天室那样不管有没有人发言,都会定时刷新.而是当有人发言时,屏幕上才会出现新聊天内容, ...

最新文章

  1. 有一群200w年薪的朋友是什么感觉?谈一谈入学中国科学院大学的几点感受吧
  2. 又是找 Bug 的一天! | 每日趣闻
  3. 轻松管理Win 2003的“远程桌面”
  4. linux版本之redhat9------终端中文软件zhcon0.2.6的安装及使用
  5. Luogu 2827 [NOIP2016] 蚯蚓
  6. webpack 原理图_webpack打包原理
  7. MatConvnet工具箱文档翻译理解(1)
  8. 服务器的共享文件夹怎么隐藏,Server200服务器隐藏共享文件夹 隐藏共享文件的方法...
  9. Ubuntu安装并使用sogou输入法
  10. 在线安装php,CentOS在线安装PHP|dayblog-天天博客|PHP交流,PHP技术,PHP博客,博客交流,dayblog,blog,天天博客...
  11. liblfds 测试
  12. ubuntu 虚拟机上的 django 服务,在外部Windows系统上无法访问
  13. confirm的意思中文翻译_confirm的中文意思
  14. kdtree java_KdTree理解与实现(Java)
  15. JavaScript文档对象模型DOM节点操作之兄弟节点(4)
  16. Linux下安装gcc环境
  17. Java删除服务器上的文件
  18. Webpack入门教程十八
  19. 嵌入式Linux的内核编译
  20. oracle11g ora12560,Oracle11gORA-609TNS12537TNS12560

热门文章

  1. 【秋招总结】面试相关-基础概念
  2. 更换路由器之后,ftp无法上传和下载数据
  3. 第一届雷达学报博士论坛学术报告目次
  4. Excel VBA设计测绘工程电子计算表格从入门到入土(待完结)- 开发方法举例
  5. VBS 计算汉字笔画数
  6. 随笔记:PPT图形布尔运算:合并形状
  7. STARTING POINT TIER 2 Oopsie
  8. Python 网络爬虫实战:爬取 B站《全职高手》20万条评论数据
  9. python hashlib_python加密之hashlib
  10. 分布式事务(三)Seata分布式事务框架-AT模式介绍