基于TCP的网络实时聊天室(socket通信案例)
java团长
共 10908字,需浏览 22分钟
·
2020-12-11 11:32
来源:https://blog.csdn.net/Charzous
开门见山
(1) L(list):查看当前上线用户;
(2) G(group):群聊;
(3) O(one-one):私信;
(4) E(exit):退出当前聊天状态;
(5) bye:离线;
(6) H(help):查看帮助
本篇将详细记录网络实时聊天室的实现步骤
一、数据结构Map
二、保证线程安全
三、群聊核心方法
private void sendToMembers(String msg,String hostAddress,Socket mySocket) throws IOException{
PrintWriter pw;
OutputStream out;
Iterator iterator=users.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry entry=(Map.Entry) iterator.next();
Socket tempSocket = (Socket) entry.getKey();
String name = (String) entry.getValue();
if (!tempSocket.equals(mySocket)){
out=tempSocket.getOutputStream();
pw=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
pw.println(hostAddress+":"+msg);
}
}
}
private void sendToOne(String msg,String hostAddress,Socket another) throws IOException{
PrintWriter pw;
OutputStream out;
Iterator iterator=users.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry entry=(Map.Entry) iterator.next();
Socket tempSocket = (Socket) entry.getKey();
String name = (String) entry.getValue();
if (tempSocket.equals(another)){
out=tempSocket.getOutputStream();
pw=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
pw.println(hostAddress+"私信了你:"+msg);
}
}
}
四、聊天室具体设计
private ConcurrentHashMapString > users=new ConcurrentHashMap();
0、用户登录服务器
pw.println("From 服务器:欢迎使用服务!");
pw.println("请输入用户名:");
String localName = null;
while ((hostName=br.readLine())!=null){
users.forEach((k,v)->{
if (v.equals(hostName))
flag=true;//线程修改了全局变量
});
if (!flag){
localName=hostName;
users.put(socket,hostName);
flag=false;
break;
}
else{
flag=false;
pw.println("该用户名已存在,请修改!");
}
}
1、查看当前上线用户
if (msg.trim().equalsIgnoreCase("L")){
users.forEach((k,v)->{
pw.println("用户:"+v);
});
continue;
}
2、群聊
else if (msg.trim().equals("G")){
pw.println("您已进入群聊。");
while ((msg=br.readLine())!=null){
if (!msg.equals("E")&&users.size()!=1)
sendToMembers(msg,localName,socket);
else if (users.size()==1){
pw.println("当前群聊无其他用户在线,已自动退出!");
break;
}
else {
pw.println("您已退出群组聊天室!");
break;
}
}
}
3、私信
//一对一私聊
else if (msg.trim().equalsIgnoreCase("O")){
pw.println("请输入私信人的用户名:");
String name=br.readLine();
//查找map中匹配的socket,与之建立通信
//有待改进,处理输入的用户名不存在的情况
users.forEach((k, v)->{
if (v.equals(name)) {
isExist=true;//全局变量与线程修改问题
}
});
//已修复用户不存在的处理逻辑
Socket temp=null;
for(Map.Entry<Socket,String> mapEntry : users.entrySet()){
if(mapEntry.getValue().equals(name))
temp = mapEntry.getKey();
// System.out.println(mapEntry.getKey()+":"+mapEntry.getValue()+'\n');
}
if (isExist){
isExist=false;
//私信后有一方用户离开,另一方未知,仍然发信息而未收到回复,未处理这种情况
while ((msg=br.readLine())!=null){
if (!msg.equals("E")&&!isLeaved(temp))
sendToOne(msg,localName,temp);
else if (isLeaved(temp)){
pw.println("对方已经离开,已断开连接!");
break;
}
else{
pw.println("您已退出私信模式!");
break;
}
}
}
else
pw.println("用户不存在!");
}
4、退出当前聊天状态
//判断用户是否已经下线
private Boolean isLeaved(Socket temp){
Boolean leave=true;
for(Map.Entry
String > mapEntry : users.entrySet()) {if (mapEntry.getKey().equals(temp))
leave = false;
}
return leave;
}
5、离线
if (msg.trim().equalsIgnoreCase("bye")) {
pw.println("From 服务器:服务器已断开连接,结束服务!");
users.remove(socket,localName);
sendToMembers("我下线了",localName,socket);
System.out.println("客户端离开。");//加当前用户名
break;
}
6、查看帮助
else if (msg.trim().equalsIgnoreCase("H")){
pw.println("输入命令功能:(1)L(list):查看当前上线用户;(2)G(group):进入群聊;(3)O(one-one):私信;(4)E(exit):退出当前聊天状态;(5)bye:离线;(6)H(help):帮助");
continue;//返回循环
}
五、聊天室服务完整代码
class Handler implements Runnable {
private Socket socket;
public Handler(Socket socket) {
this.socket = socket;
}
public void run() {
//本地服务器控制台显示客户端连接的用户信息
System.out.println("New connection accept:" + socket.getInetAddress().getHostAddress());
try {
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
pw.println("From 服务器:欢迎使用服务!");
pw.println("请输入用户名:");
String localName = null;
while ((hostName=br.readLine())!=null){
users.forEach((k,v)->{
if (v.equals(hostName))
flag=true;//线程修改了全局变量
});
if (!flag){
localName=hostName;
users.put(socket,hostName);
flag=false;//可能找出不一致问题
break;
}
else{
flag=false;
pw.println("该用户名已存在,请修改!");
}
}
// System.out.println(hostName+": "+socket);
sendToMembers("我已上线",localName,socket);
pw.println("输入命令功能:(1)L(list):查看当前上线用户;(2)G(group):进入群聊;(3)O(one-one):私信;(4)E(exit):退出当前聊天状态;(5)bye:离线;(6)H(help):帮助");
String msg = null;
//用户连接服务器上线,进入聊天选择状态
while ((msg = br.readLine()) != null) {
if (msg.trim().equalsIgnoreCase("bye")) {
pw.println("From 服务器:服务器已断开连接,结束服务!");
users.remove(socket,localName);
sendToMembers("我下线了",localName,socket);
System.out.println("客户端离开。");//加当前用户名
break;
}
else if (msg.trim().equalsIgnoreCase("H")){
pw.println("输入命令功能:(1)L(list):查看当前上线用户;(2)G(group):进入群聊;(3)O(one-one):私信;(4)E(exit):退出当前聊天状态;(5)bye:离线;(6)H(help):帮助");
continue;//返回循环
}
else if (msg.trim().equalsIgnoreCase("L")){
users.forEach((k,v)->{
pw.println("用户:"+v);
});
continue;
}
//一对一私聊
else if (msg.trim().equalsIgnoreCase("O")){
pw.println("请输入私信人的用户名:");
String name=br.readLine();
//查找map中匹配的socket,与之建立通信
users.forEach((k, v)->{
if (v.equals(name)) {
isExist=true;//全局变量与线程修改问题
}
});
//已修复用户不存在的处理逻辑
Socket temp=null;
for(Map.Entry
mapEntry : users.entrySet()){ if(mapEntry.getValue().equals(name))
temp = mapEntry.getKey();
}
if (isExist){
isExist=false;
//私信后有一方用户离开,另一方未知,仍然发信息而未收到回复,未处理这种情况
while ((msg=br.readLine())!=null){
if (!msg.equals("E")&&!isLeaved(temp))
sendToOne(msg,localName,temp);
else if (isLeaved(temp)){
pw.println("对方已经离开,已断开连接!");
break;
}
else{
pw.println("您已退出私信模式!");
break;
}
}
}
else
pw.println("用户不存在!");
}
//选择群聊
else if (msg.trim().equals("G")){
pw.println("您已进入群聊。");
while ((msg=br.readLine())!=null){
if (!msg.equals("E")&&users.size()!=1)
sendToMembers(msg,localName,socket);
else if (users.size()==1){
pw.println("当前群聊无其他用户在线,已自动退出!");
break;
}
else {
pw.println("您已退出群组聊天室!");
break;
}
}
}
else
pw.println("请选择聊天状态!");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
六、效果演示:基于TCP的网络实时聊天室
结语
评论