关键词:
Tcp多人聊天室实现
客户端:
-
功能:
- 数据发送
- 数据接收
-
技术:
- socket
- 输入流和输出流
- 多线程,客户端功能模块有两个线程
-
聊天方式:
-
群聊
-
私聊
私聊方法:@服务器用户id号:msg
-
服务器:
-
功能:
- 数据转发
- 用户注册
-
技术:
- ServerSocket
- 每一个用户对应的Socket对象
- 多线程同时在线
- HashMap<Integer,用户>
-
数据转发:
私聊前缀判断
群聊所有人发送
资源关闭问题
代码中操作了大量的输入流和输出流,都需要进行关闭操作
DataInputStream, DataOutputStream, BufferedReader, Socket
以上这些资源都是Closeable接口的实现类
client
Clent.java
package com.qfedu.a_charoom.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Anonymous
* @description
* @date 2020/3/9 10:01
*
* 完成内容
* 1. 连接服务器
* 2. 启动接收端线程和发送端线程
* 需要注意:
* 提供给服务器一个用户名,这里是在连接Socket获取之后,第一次发送数据
* 给服务器时需要提供的,也就是第一次启动Send发送线程完成的
*/
public class Client
public static void main(String[] args)
// 从键盘上获取用户输入的数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Socket socket = null;
String name = null;
try
System.out.println("请输入你的用户名:");
name = br.readLine();
if ("".equals(name))
return;
// 存在连接异常情况,考虑捕获异常处理
socket = new Socket("192.168.31.154", 8888);
catch (IOException e)
System.err.println("连接失败!!!");
try
br.close();
catch (IOException ex)
ex.printStackTrace();
System.exit(0);
// 使用线程池启动两个线程,一个是发送一个接受
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new ClientSend(socket, name));
pool.submit(new ClientReceive(socket));
ClientSend.java
package com.qfedu.a_charoom.client;
import com.qfedu.a_charoom.util.CloseUtil;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* @author Anonymous
* @description 发送端
* @date 2020/3/9 10:01
*
* 这里需要输出流
* 输出流对象需要通过Socket获取,在当前客户端连接到服务器之后,Socket对象存在
* 的情况下才可以启动的
* 成员变量:
* 输出流
* DataOutputStream
* 需要一个从键盘上输入数据使用的输入流,获取用户输入信息
* BufferedReader
* 标记是否连接
* 构造方法:
* 需要Socket作为当前构造的参数,第一次访问服务器需要带有用户名,用于注册
* 成员方法:
* 发送数据给服务器
* 从键盘上获取用户的数据
*/
public class ClientSend implements Runnable
/**
* 基于Socket获取的输出流对象,用于发送数据给服务器
*/
private DataOutputStream outputStream;
/**
* 从键盘上获取用户输入的BufferedReader字符缓冲输入流
*/
private BufferedReader console;
/**
* 是否连接
*/
private boolean connection;
/**
* 使用客户端和服务器连接使用的Socket对象,和用户指定的用户名创建
* ClientSend线程对象,同时初始化输出流和键盘录入输入流对象
*
* @param socket 客户端连接服务器对应的Socket对象
* @param userName 用户指定的用户名,用于服务器注册
*/
public ClientSend(Socket socket, String userName)
// 初始化输出流和输出流
try
outputStream = new DataOutputStream(socket.getOutputStream());
console = new BufferedReader(new InputStreamReader(System.in));
// 发送用户名给服务器注册 需要完成一个send方法
send(userName);
connection = true;
catch (IOException e)
e.printStackTrace();
// 连接标记关闭,同时处理对应的输入流和键盘录入流
connection = false;
CloseUtil.closeAll(outputStream, console);
/**
* 从键盘上获取用户输入的数据
*
* @return 用户输入的数据字符串形式
*/
public String getMsgFromConsole()
String msg = null;
try
// 从键盘上读取一行数据
msg = console.readLine();
catch (IOException e)
e.printStackTrace();
/*
发生异常:
1. connection连接标记改成false
2. 不是null需要关闭资源
outputStream, console
*/
connection = false;
CloseUtil.closeAll(outputStream, console);
return msg;
/**
* 发送数据给服务器
*
* @param msg 需要发送给服务器的数据
*/
public void send(String msg)
// 如果这里数据为null,或者"" 不发送
try
if (msg != null && !"".equals(msg))
outputStream.writeUTF(msg);
outputStream.flush();
catch (IOException e)
e.printStackTrace();
/*
发生异常:
1. connection连接标记改成false
2. 不是null需要关闭资源
outputStream, console
*/
connection = false;
CloseUtil.closeAll(outputStream, console);
/**
* 线程代码,只要当前connection是连接状态,一直执行send和getMsgFromConsole
*/
@Override
public void run()
while (connection)
send(getMsgFromConsole());
ClientReceive.java
package com.qfedu.a_charoom.client;
import com.qfedu.a_charoom.util.CloseUtil;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* @author Anonymous
* @description
* @date 2020/3/9 10:01
*
* 客户端接收数据线程
*
* 这里需要输入流
* 输入流是通过Socket对象获取的,也就是在客户端连接服务器之后才可以获取到输入流
* 成员变量:
* 输入流
* DataInputStream
* 标记是否连接
* 构造方法:
* 需要Socket作为当前构造的参数
* 成员方法:
* 从服务器接收数据,展示
*/
public class ClientReceive implements Runnable
/**
* 用于接收数据的输入流对象
*/
private DataInputStream inputStream;
/**
* 是否连接状态标记
*/
private boolean connection;
/**
* 根据客户端连接服务器对应的Socket对象获取输入流对象
*
* @param socket 客户端连接服务器对应的Socket
*/
public ClientReceive(Socket socket)
try
inputStream = new DataInputStream(socket.getInputStream());
connection = true;
catch (IOException e)
e.printStackTrace();
connection = false;
/**
* 接收数据并且展示
*/
public void receive()
String msg = null;
try
msg = inputStream.readUTF();
catch (IOException e)
e.printStackTrace();
/*
接收数据出现异常:
连接标记修改
不是null关闭资源
*/
connection = false;
CloseUtil.closeAll(inputStream);
System.out.println(msg);
/**
* 线程核心方法,只要连接状态OK,始终保存接收状态
*/
@Override
public void run()
while (connection)
receive();
Server
Server.java
package com.qfedu.a_charoom.server;
import com.qfedu.a_charoom.util.CloseUtil;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
/**
* @author Anonymous
* @description
* @date 2020/3/9 11:12
*
* 服务器需要转发数据
* 1. 数据转发
* 群聊
* 私聊
* 2. 用户注册
* 用户 ==> class
*
* 私聊,群聊属于用户功能
* 用户这里看做是一个类
* 成员变量
* 输入流
* 输出流
* 用户ID
* 用户名
* 连接状态标记
* 成员方法:
* 接收方法
* 利用客户端连接服务器对应的Socket得到输入流接收用户发送的数据
* 发送方法
* 群聊
* 遍历整个有效用户
* 私聊
* 找到对应用户
* 利用客户端连接服务器对应的Socket得到输出流发送数据
* 【成员内部类】
* 用户做出一个成员内部类
* 作为Server服务器类的一个成员变量内部类
*
* 用户注册流程
* 1. ServerSocket Accept客户端连接,获取对应Socket对象
* 2. 记录在线人数,创建一个新的UserSocket
* 3. Map中映射对应的UserSocket,Key 为ID, Value是UserSocket
*
*/
public class Server
/**
* 用户ID和UserSocket映射
*/
private HashMap<Integer, UserSocket> userMap;
/**
* 累加访客人数
*/
private static int count = 0;
/**
* Server构造方法,用于初始化底层保存数据的HashMap双边队列
*/
public Server()
userMap = new HashMap<>();
/**
* 服务器启动方法
*
* @throws IOException IO异常
*/
public void start() throws IOException
// 启动服务器,同时监听8888端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器启动");
// 服务器始终处于一个保存连接的状态
while (true)
// 接收客户端请求,得到一个Socket对象
Socket socket = serverSocket.accept();
// 创建UserSocket用于注册,并且保存到userMap当中
count += 1;
UserSocket userSocket = new UserSocket(count, socket);
userMap.put(count, userSocket);
// 启动当前UserSocket服务
new Thread(userSocket).start();
/**
* @author Anonymous
* @description 用户Socket类,需要完成绑定操作,并且是一个线程类
* @date 2020/3/9 11:23
*/
class UserSocket implements Runnable
/*
* 对应当前客户端连接服务器对应Socket生成输入流和输出流
*/
private DataInputStream inputStream;
private DataOutputStream outputStream;
/**
* 用户ID号,是当前用户的唯一表示,不可以重复
*/
private int userId;
/**
* 用户名,用于注册,同时在发送数据时给予其他用户标记
*/
private String userName;
/**
* 是否连接状态标记
*/
private boolean connetion;
/**
* 创建UserSocket对象,需要的参数使用userID,和对应的Socket对象
*
* @param userId 当前用户的ID号
* @param socket 客户端连接服务器对应的Socket
*/
public UserSocket(int userId, Socket socket)
this.userId = userId;
try
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
connetion = true;
catch (IOException e)
e.printStackTrace();
connetion = false;
try
// 用户在创建Send线程时,首先会将用户的名字发送给服务器
assert inputStream != null;
this.userName = inputStream.readUTF();
// 广播告知所有人,ID:XXX 姓名: XXX 上线 【群聊,系统广播】
sendOther("ID:" + this.userId + " " + this.userName + "来到直播间", true);
// 服务器告诉当前客户端,你已经进入聊天室
send("欢迎来到聊天室");
catch (IOException e)
e.printStackTrace();
/**
* 接收客户端发送的数据,用于转发操作
*
* @return 用户发送的数据
*/
public String receive()
String msg = null;
try
// 接收用户发送到服务器需要服务器转发的数据
msg = inputStream.readUTF();
catch (IOException e)
e.printStackTrace();
/*
发生异常:
1. 连接状态修改
2. 关闭资源
3. 删除在userMap中对应的数据【留下】
对号是书签 快捷键 F11
查看当前项目所有的书签 ALT + 2
*/
connetion = false;
CloseUtil.closeAll(inputStream, outputStream);
return msg;
/**
* 发送数据到客户端
*
* @param msg 需要发送的数据
*/
public void send(String msg)
try
outputStream.writeUTF(msg);
outputStream.flush();
catch (IOException e)
e.printStackTrace();
/*
发生异常:
1. 连接状态修改
2. 关闭资源
3. 删除在userMap中对应的数据
*/
connetion = false;
CloseUtil.closeAll(inputStream, outputStream);
/*
这里需要一个方法
1. 私聊判断
2. 群聊
这里需要根据msg进行判断
1. 如果是@数字: 前缀
私聊
通过HashMap --> ID --> UserSocket --> 转发消息
2. 非@数字:开头群聊
a. 系统播报
b. 私人发送
这里需要做一个标记
获取所有在线用户,判断除自己之外,其他人转发消息
*/
/**
* 转发数据判断方法,msg需要处理,选择对应的私聊和群发,同时要判断是否是系统发送消息
*
* @param msg 需要转发的消息
* @param sys 系统标记
*/
public void sendOther(String msg, boolean sys)
if (msg.startsWith("@") && msg.contains(":"))
// 私聊
// @1:XXXXXX
Integer id = Integer.parseInt(msg.substring(1, msg.indexOf(":")));
String newMsg = msg.substring(msg.indexOf(":"));
UserSocket userSocket = userMap.get(id);
// 如果没有对应的UserSocket用户存在,无法发送消息
if (userSocket != null)
// ID:1 小磊磊悄悄的对你说:XXXX
userSocket.send("ID:" + this.userId + " " + this.userName + "悄悄的对你说" + msg);
else
// 群聊
// 从userMap中获取对应的所有value,也就是所有UserSocket对象Collection集合
Collection<UserSocket> values = userMap.values();
for (UserSocket userSocket : values)
// 不需要将消息发送给自己
if (userSocket != this)
// 判断是不是系统消息
if (sys)
userSocket.send("系统公告:" + msg);
else
userSocket.send("ID:" + this.userId + " " + this.userName + msg);
/**
* 线程代码
*/
@Override
public void run()
while (connetion)
// 使用receive收到的消息作为参数,同时标记非系统消息,调用sendOther
sendOther(receive(), false);
public static void main(String[] args)
// 启动服务器!!!
Server server = new Server();
try
server.start();
catch (IOException e)
e.printStackTrace();
Util
CloseUtil.java
狂神说java笔记--网络编程部分笔记(代码片段)
传送门==>B站遇见狂神说—网络编程笔记和练习只是跟着视频整理的;有的知识点并没有整理进来.ml1.什么是计算机网络2.网络通信的两个要素3.IP地址4.port:端口5.通信协议6.TCP实现聊天7.TCP文件上传实现8.UDP消息发送9.UDP实现... 查看详情
1500行代码!拥有自己的聊天室------socket聊天室实现(gui,线程,tcp)(代码片段)
Java学习打卡:第三十一、二天【完成sococket聊天室搭建项目】内容导航项目需求分析基础分析项目部分代码摘要Dao的链表存储实现ServerListenServerReceive再看一下客户端的ClientReceive项目问题选择框中出现的不是用户名服务端点... 查看详情
node.js网络编程(下)实现tcpudpwebsocket的创建(代码片段)
...端WebSocket服务器与客户端WebSocket实现机制WebSocket构建实时聊天室TCP服务器与客户端TCP基础TCP协议:传输控制协议,提供面向连接的、可靠的数据传输服务面向连接:数据传输之前, 查看详情
网络编程之java简易聊天室实现
最近浅学习了一些关于网络编程方面的知识,视频是跟着狂神学习的,可能学习的不是很深说到网络,相信大家都对TCP、UDP和HTTP协议这些都不是很陌生,学习这部分应该先对端口、Ip地址这些基础知识有一定了解,后面我们都是... 查看详情
网络编程项目——多人聊天室->双人聊天(代码片段)
一、Coding前的思考和步骤梳理1.又开始思考这个问题,拿到一个需求,是调用java底层的代码写1000行实现,还是直接调第三方库写50行实现?都一样。发现第一次写shell的时候其实思考过。 2.应该用传输层协议还是... 查看详情
026.3网络编程tcp聊天(代码片段)
分为客户端和服务端,分别进行收发操作##########################################################################客户端:###思路:1、建立tcp客户端服务 1.1因为是面向连接,必须有连接才有通信 1.2在创建客户端时,就... 查看详情
socket编程(在控制台模拟聊天功能)(代码片段)
目录TCP简单示例TCP模拟聊天UDP简单示例UDP模拟聊天@服务器端(1)创建ServerSocket对象,绑定监听端口;(2)通过accept()方法监听客户端请求;(3)连接建立后,通过输入流读取客户端发送的请求信息;(4)通过输出流向客户端发送相应信息... 查看详情
java利用tcp编程实现简单聊天室
...体可以去尚学堂官网观看视频学习一、实现思路 实现聊天室的最核心部分就是JAVA的TCP网络编程。 TCP传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,在Java中我们利用ServerSocket类来建立服务端,... 查看详情
java基础——tcp网络编程实现文件的上传(代码片段)
直接上代码:importjava.io.BufferedInputStream;importjava.io.FileInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.InetAddress;importjava.net.So 查看详情
利用java的socket实现一个简单hello/hi聊天程序(代码片段)
...在这个程序里,我学习到了怎么用socket套接套接字来进行编程。简单理解了一些关于socket套接字和底层调用的关系。关于java的封装思想,我学会了一些东西,java里真的是万物皆对象。还学到了一点多线程的知识。 TCP 在... 查看详情
qt学习_网络编程_tcp通信聊天(代码片段)
网络编程TCP通信1.用到的类QTcpServer公共函数:void close()QString errorString()constbool isListening()constbool listen(constQHostAddress&address=QHostAddress::Any,quint16port=0)QHostAddre 查看详情
java通过socket实现tcp编程(代码片段)
简介TCP简介TCP(TransmissionControlProtocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数... 查看详情
tcp与udp实例(代码片段)
...以参照之前的博文,这里只是采用send/recv函数实现TCP编程,同时实现了UDP编程一、相关API1.send函数ssize_tsend(intsockfd,constvoid*buf,size_tlen,intflags);sockfd指定发送端套接字描述符;buf指明一个存放应用程序要发送数据的缓 查看详情
tcp与udp实例(代码片段)
...以参照之前的博文,这里只是采用send/recv函数实现TCP编程,同时实现了UDP编程一、相关API1.send函数ssize_tsend(intsockfd,constvoid*buf,size_tlen,intflags);sockfd指定发送端套接字描述符;buf指明一个存放应用程序要发送数据的缓 查看详情
第97题java高级技术-网络编程16(简易聊天室11:实现客户端群聊)(代码片段)
回城传送–》《JAVA筑基100例》文章目录零、前言一、题目描述二、解题思路三、代码详解四、推荐专栏五、示例源码下载零、前言今天是学习JAVA语言打卡的第97天,每天我会提供一篇文章供群成员阅读(不需要订阅付... 查看详情
第98题java高级技术-网络编程17(简易聊天室12:实现客户端一对一聊天)(代码片段)
回城传送–》《JAVA筑基100例》文章目录零、前言一、题目描述二、解题思路三、代码详解多学一个知识点四、推荐专栏五、示例源码下载零、前言今天是学习JAVA语言打卡的第98天,每天我会提供一篇文章供群成员阅读... 查看详情
golang学习十:网络编程(代码片段)
...2.获取文件属性:3.客户端实现4.服务端实现:四、示例-并发聊天室:1模块简述2.广播用户上线3.广播用户消息4.展示在线用户:5.修改用户名:5.用户退出与超时处理:一、网络协议:1.典型协议:传输层:常见协议有TCP/UDP协议;应用层:常见的... 查看详情
java案例:基于tcp的简单聊天程序(代码片段)
文章目录一、如何实现TCP通信二、编写C/S架构聊天程序(一)编写服务器端程序-Server.java(二)编写客户端程序-Client.java(三)测试服务器端与客户端能否通信(四)程序优化思路-服务器端采用多... 查看详情