Server 服务端

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;/*** 服务端*/
public class Server {private static final int SERVER_PORT=8080;//使用CrazyitMap对象来保存每个客户名字和对应输出流之间的对应关系public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>();public void init(){try( //建立监听的ServerSocketServerSocket ss=new ServerSocket(SERVER_PORT)){//采用死循环来不断地接收来自客户端的请求while(true){Socket socket=ss.accept();new ServerThread(socket).start();}}//如果抛出异常catch (IOException ex){System.out.println("服务器启动失败,是否端口"+SERVER_PORT+"已被占用");}}public static void main(String[] args){Server server=new Server();server.init();}
}

ServerThread 服务端线程

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;/*** 服务端线程*/
public class ServerThread extends Thread {private Socket socket;BufferedReader br = null;PrintStream ps = null;//定义一个构造器,用于接收一个Socket来创建ServerThread线程public ServerThread(Socket socket) {this.socket = socket;}public void run() {try {//获取该Socket对应的输入流br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获取该Socket对应的输出流ps = new PrintStream(socket.getOutputStream());String line = null;while ((line = br.readLine()) != null) {//如果读到的行以CrazyitProtocol.USER_ROUND开始,并以其结束//则可以确定读到的是用户登录的用户名if (line.startsWith(CrazyitProtocol.USER_ROUND) && line.endsWith(CrazyitProtocol.USER_ROUND)) {//得到真实消息String userName = getRealMsg(line);//如果用户名重复if (Server.clients.map.containsKey(userName)) {System.out.println("重复");ps.println(CrazyitProtocol.NAME_REP);} else {System.out.println(userName+"上线");ps.println(CrazyitProtocol.LOGIN_SUCCESS);Server.clients.put(userName, ps);}}//如果读到的行以CrazyitProtocol.PRIVATE_ROUND开始,//则可以确定是私聊信息,私聊信息只向特定的输入流发送else if (line.startsWith(CrazyitProtocol.PRIVATE_ROUND) && line.endsWith(CrazyitProtocol.PRIVATE_ROUND)) {//得到真实的消息String userAndMsg = getRealMsg(line);//以SPLIT_SIGN分割字符串,前半是私聊用户,后半是聊天信息String user = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0];String msg = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1];//获取私聊用户对应的输出流,并发送私聊信息Server.clients.map.get(user).println(Server.clients.getKeyByValue(ps) + "悄悄对你说:" + msg);}//公聊要向每一个Socket发送else {//得到真实消息String msg = getRealMsg(line);//遍历clients中的每个输出流for (PrintStream clientPs : Server.clients.valueSet()) {clientPs.println(Server.clients.getKeyByValue(ps) + "说:" + msg);}}}}//捕获到异常后,表明Socket对应的客户端已经出现了问题//所以程序将其对应的输出流从Map中删除catch (IOException e) {Server.clients.removeByValue(ps);System.out.println(Server.clients.map.size());//关闭网络,IO资源try {if (br != null) {br.close();}if (ps != null) {ps.close();}if (socket != null) {socket.close();}} catch (IOException ex) {ex.printStackTrace();}}}//将读到的内容去掉前后协议字符,恢复成真实数据private String getRealMsg(String line) {return line.substring(CrazyitProtocol.PROTOCOL_LEN,line.length()-CrazyitProtocol.PROTOCOL_LEN);}
}

Client 客户端

import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;/*** 客户端*/
public class Client {private static final int SERVER_PORT=8080;private Socket socket;private PrintStream ps;private BufferedReader brServer;private BufferedReader keyIn;public void init(){try{//初始化代表键盘的输入流keyIn=new BufferedReader(new InputStreamReader(System.in));//连接到服务器端socket=new Socket("127.0.0.1",SERVER_PORT);//获取该Socket对应的输入流和输出流ps=new PrintStream(socket.getOutputStream());brServer=new BufferedReader(new InputStreamReader(socket.getInputStream()));String tip=" ";//采用不断循环地弹出对话框要求输入用户名while(true){String userName= JOptionPane.showInputDialog(tip+"输入用户名");//用户输入的用户名前后增加协议字符串后发送ps.println(CrazyitProtocol.USER_ROUND+userName+CrazyitProtocol.USER_ROUND);//读取服务器端的响应String result=brServer.readLine();//如果用户名重复,则开始下一次循环if (result.equals(CrazyitProtocol.NAME_REP)){tip="用户名重复,请重试";continue;}//服务器端登录成功if (result.equals(CrazyitProtocol.LOGIN_SUCCESS)){break;}}}//捕获到异常,关闭网络资源,并退出该程序catch (UnknownHostException ex){System.out.println("找不到远程服务器,请确定服务器已经启动");closeRs();System.exit(1);}catch(IOException ex){System.out.println("网络异常,请重新登录");closeRs();System.exit(1);}//以该Socket对应的输入流启动ClientThread线程new ClientThread(brServer).start();}//定义一个读取键盘输出,并以网络发送的方法private void readAndSend(){try{//不断读取键盘输入String line=null;while ((line=keyIn.readLine())!=null){//如果发送的信号中有冒号,并以//开头,则认为想发送私聊信息if (line.indexOf(":")>0&&line.startsWith("//")){line=line.substring(2);ps.println(CrazyitProtocol.PRIVATE_ROUND+line.split(":")[0]+CrazyitProtocol.SPLIT_SIGN+line.split(":")[1]+CrazyitProtocol.PRIVATE_ROUND);}else{ps.println(CrazyitProtocol.MSG_ROUND+line+CrazyitProtocol.MSG_ROUND);}}}catch (IOException ex){System.out.println("网络通信异常!请重新登录");closeRs();System.exit(1);}}//关闭Socket,输入流,输出流的方法private void closeRs(){try{if (keyIn!=null){ps.close();}if (brServer!=null){ps.close();}if (ps!=null){ps.close();}if (socket!=null){keyIn.close();}}catch (IOException ex){ex.printStackTrace();}}public static void main(String[] args){Client client=new Client();client.init();client.readAndSend();}
}

ClientThread 客户端线程

import java.io.BufferedReader;
import java.io.IOException;/*** 客户端线程*/
public class ClientThread extends Thread {//该客户端线程负责处理输入流BufferedReader br=null;//使用一个网络输入流来创建客户端线程public ClientThread(BufferedReader br){this.br=br;}public void run(){try{String line=null;//不断地从输入流中读取数据,并将这些数据打印输出while((line=br.readLine())!=null){System.out.println(line);}}catch (IOException ex){ex.printStackTrace();}finally {try {if (br!=null){br.close();}}catch (IOException ex){ex.printStackTrace();}}}
}

CrazyitMap 容器

import java.util.*;/*** map容器** @param <k>* @param <v>*/
public class CrazyitMap<k,v> {//创建一个线程安全的HashMappublic Map<k,v> map= Collections.synchronizedMap(new HashMap<k,v>());//根据value来删除指定项public synchronized void removeByValue(Object value){for (Object key:map.keySet()){if (map.get(key)==value){map.remove(key);break;}}}//获取所有value组成的Set集合public synchronized Set<v> valueSet(){Set<v> result=new HashSet<v>();//将map中的所有value添加到result集合中map.forEach((key,value)->result.add(value));return result;}//根据value查找keypublic synchronized k getKeyByValue(v value){//遍历所有key组成的集合for (k key:map.keySet()){//如果指定key对应的value与被搜索的value相同,则返回对应的keyif(map.get(key)==value||map.get(key).equals(value)){return key;}}return null;}//实现put()方法,该方法不允许value重复public synchronized v put(k key,v value){//遍历所有value组成的集合for (v val:valueSet()){//如果某个value与试图放入集合的value相同//则抛出一个RuntimeException异常if (val.equals(value)&&val.hashCode()==value.hashCode()){throw new RuntimeException("MyMap实例不允许有重复的value");}}return map.put(key,value);}
}

CrazyitProtocol 接口

/*** 自定义接口*/
public interface CrazyitProtocol {//定义协议字符串的长度int PROTOCOL_LEN=2;//下面是一些协议字符串,服务器端和客户端交换的信息都应该在前后添加这种特殊字符串String MSG_ROUND="ηθ";String USER_ROUND="∏∑";String LOGIN_SUCCESS="1";String NAME_REP="-1";String PRIVATE_ROUND="★【";String SPLIT_SIGN="卐";
}

测试

1、启动服务端

2、启动客户端 (上线)

3 、群发

4、私聊 (//名字:)

Java 实现局域网聊天室功能(私聊,群聊)相关推荐

  1. SpringBoot与webSocket实现在线聊天室——实现私聊+群聊+聊天记录保存

    SpringBoot与webSocket实现在线聊天室--实现私聊+群聊+聊天记录保存 引用参考:原文章地址:https://blog.csdn.net/qq_41463655/article/det ...

  2. javaWeb实现聊天室(私聊+群聊)

    写在前面 近几天,迎来了第一个小项目,不做不知道,一做吓一跳.好多知识都掌握的不够扎实,看似会了,但其实似懂非懂,不能真正掌握原理,导致使用起来错误百出.而且深深体会到,知识只有到用时方恨少,一个简单 ...

  3. java网络程序设计 聊天室之私聊、群聊和清屏功能的实现

    TCP聊天室实现了私聊.群聊和清屏的功能,简陋的UI界面,一个服务器端,支持多个客户端之间的通信. 项目代码:https://pan.baidu.com/s/17iegRam4KnWvcWHw3mvp ...

  4. 基于netty的在线聊天室,支持群聊和私聊——【一】基本功能介绍和nginx配置

    netty虽然可以实现聊天室的功能.但完整的做下来,还是要自己去封装很多东西,尤其是客户端和服务器通信的数据格式,服务端消息派发器的设计.这一点就比spring 的websocket over sto ...

  5. Netty+Android搭建一个简易聊天室(实现群聊和私聊)

    零,前言 JRBM项目中无论是好友私聊,公开聊天室,还是比赛平台都需要用到长连接,之前没有接触过网络通信等知识,更别说框架了,因此直接上手netty确实有些困难,在前期主要是在b站上看(https:/ ...

  6. android下的XMPP对应smack-4.2.1,实现登录,注册,发单聊,加聊天室,发群聊等简单功能

    1.API地址:点击打开链接 2.openfire,spark下载:点击打开链接 3.效果图 4.引用的JAR: compile files('libs/fscontext.jar') compile ...

  7. java TCP 实现聊天室功能 多线程实现一个服务器端和多个客户端之间的聊天

    package 实验702;import java.net.Socket;public class Client {public static void main(String[] args) {So ...

  8. java实现qq聊天室功能

    一下是服务器端源码,供大家参考 下面是main.java package com;public class Main {public static void main(String args[]) { ...

  9. Java实现多线程局域网聊天室

    Java多线程局域网聊天室 局域网聊天室 1.TCP 2.套接字 3.C/S架构 4.多线程 5.服务器 客户端 局域网聊天室 在学习了一个学期的java以后,觉得java真是博大精深,彻底放弃了因为 ...

  10. 在线聊天室的消息单聊的实现——springboot整合WebSocket(二)

    一.声明 项目的搭建请大家移步到:在线聊天室的消息群聊的实现--springboot整合WebSocket(一) 单聊的实现是在群聊项目上进行延申改造的. 二.引入依赖 <dependency& ...

最新文章

  1. System.Transactions:实现你自己的Resource Manager
  2. smartform 与调用程序间内表数据传递方法
  3. 九、“行胜于言车胜马,负重致远向前途”
  4. tensorflow随笔-constant
  5. 树形结构与关系数据库之闭包表
  6. 今年圣诞,麋鹿第一次请假
  7. element ui table组件扩展关于列表编辑按钮的位置放置
  8. 用户权限管理shiro
  9. Android源码学习之接着浅析SystemServer
  10. 【分治】寻找主元素的3种算法
  11. 如何提高代码复用性,定义骨架?
  12. ABB机械臂手眼标定
  13. 5G无线技术基础自学系列 | CloudRAN架构
  14. wireshark不同颜色报文含义(报文颜色)
  15. c程序设计语言第五单元,一年级语文下册第五单元教案
  16. R语言 dataframe 取指定行列filter 随机取数
  17. javascript_JSON.parse() 与 JSON.stringify()_ZHOU125disorder_
  18. latex转word_最有效率的论文排版利器来了,word排版再见吧~
  19. explicit含义
  20. 【计算机科学】【2011.05】【含源码】微阵列数据的SVM分类与边缘距离分析

热门文章

  1. 微表情识别,C++实现,编译为动态链接库
  2. Blue Coat让企业在降低风险的同时利用网络应用程序
  3. 蓝桥杯--算法提高 字符串跳步
  4. 支付宝支付原理以及测试接口获取支付url
  5. Qt开发,应用程序错误,应用程序无法正常启动0xc000007b
  6. 阿兹尔海默症生物标志物(姑且叫标志物)的一些总结
  7. 浅谈测试工程师必备软技能:沟通表达篇
  8. ant design + react带有二级导航菜单自动生成
  9. 数据分析【实践】——教育行业指标体系搭建和生命周期维护
  10. Microservice Patterns 下载