当前位置:   article > 正文

Java如何实现不同局域网TCP通信+群聊+私聊(云服务器实现)_java实现跨网聊天

java实现跨网聊天

继上一篇实现群聊,现在记录私聊的实现,下一篇记录一下实现文件的传输。

socket keepalive理解 - 小小小小涛 - 博客园 (cnblogs.com)

目录

一、实现思路

1.实现思路一

2.实现思路二

二、实现代码

1.思路一(服务端代码)

2.思路二(服务端代码)

三、运行效果


一、实现思路

用户输入@name的方式发送消息(类似于QQ你@别人),但是想通过服务器端从信息中解析出这个@的人的名字不太可能,因为服务器端不知道截多长才是用户的名字。所以只能判断信息中是否包含了@name。String类的contains()方法可以做到。

服务端有以下两种思路去实现私聊

1.实现思路一

先判断用户是否@人了。

如果@人了,则发送消息的用户的服务套接字让其他的服务套接字判断这个@的名字是不是自己服务的用户的名字,如果是就转发,不是就不转发。

如果没@,则全部转发。

2.实现思路二

先判断用户是否@人了。

如果@人了,发送消息的用户的服务套接字根据@的名字让该名字的服务套接字转发。(哈希表

如果没@,则全部转发。

二、实现代码

我们的用户端只负责输入,因此只需要改变服务端的代码。(这里也只记录服务器端的代码,用户端的代码和工具类代码在上一篇)。

1.思路一(服务端代码)

  1. import java.io.Closeable;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.InetAddress;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import com.csi.qunliaoTest.OpenClient.ReceiveMsgThread;
  11. public class OpenServer {
  12. //一个列表存储服务Socket
  13. List<ClientSocket> clientSockets =new ArrayList<>();
  14. ServerSocket serverSocket = null;
  15. //构造方法中创建服务器ServerSocket
  16. public OpenServer() {
  17. try {
  18. //因为本代码是在云服务器运行,直接用InetAddress.getLocalHost()方法绑定云服务器的IP地址及端口号
  19. serverSocket = new ServerSocket(7777, 50, InetAddress.getLocalHost());
  20. System.out.println("----------服务器----------");
  21. } catch (IOException e) {
  22. //发生异常。调用自己写的Utils类关闭服务器
  23. Utils.close(serverSocket);
  24. }
  25. //ServerSocket绑定成功,开始等待用户接入
  26. if(serverSocket.isBound())
  27. acceptClient();
  28. }
  29. //等待用户,并为他生成服务Socket
  30. private void acceptClient(){
  31. //while循环让服务器一直可以接入用户,一个用户接入服务器,服务器就生成一个为该用户服务的Socket
  32. while(true) {
  33. Socket socket=null;
  34. try {
  35. //ServerScoket的accept()方法是一个阻塞方法,他会在这里等用户接入,直到有用户接入,才会运行下面的代码
  36. socket=serverSocket.accept();
  37. System.out.println("一个用户接入.....");
  38. //用ClientSocket类包装Scoket类,ClientSocket类是自己写的内部线程类,该类实现了接收和转发用户消息
  39. ClientSocket clientSocket=new ClientSocket(socket);
  40. //开启服务线程
  41. clientSocket.start();
  42. //将这个包装了服务Scoket的对象添加进列表
  43. clientSockets.add(clientSocket);
  44. } catch (IOException e) {
  45. Utils.close(socket);
  46. }
  47. }
  48. }
  49. public static void main(String[] args) {
  50. //执行服务器代码
  51. new OpenServer();
  52. }
  53. class ClientSocket extends Thread{
  54. Socket socket=null;
  55. DataInputStream dataInputStream=null;
  56. DataOutputStream dataOutputStream=null;
  57. String name = null;
  58. public ClientSocket(Socket socket) {
  59. this.socket = socket;
  60. //包装服务Socket的输入输出流,异常就调用closeScoket()方法
  61. try {
  62. dataInputStream=new DataInputStream(socket.getInputStream());
  63. } catch (IOException e) {
  64. closeSocket(dataInputStream,socket);
  65. }
  66. try {
  67. dataOutputStream=new DataOutputStream(socket.getOutputStream());
  68. } catch (IOException e) {
  69. closeSocket(dataOutputStream,socket);
  70. }
  71. }
  72. //上面的代码我们关闭的都是装饰流,因为关闭装饰流会将内部流也关闭,Socket也会因此关闭,同时我们也要将列表里对应Socket的删除
  73. public void closeSocket(Closeable...closeables) {
  74. Utils.close(closeables);
  75. System.out.println("一位用户退出");
  76. clientSockets.remove(this);
  77. }
  78. @Override
  79. public void run() {
  80. //读取用户姓名,并让列表所有用户转发欢迎信息
  81. //数据流DataIn/OutputStream的readUTF()和writeUTF(String data)要一起用,是将数据以UTF-8的编码方式发出或者接收
  82. try {
  83. //readUTF()方法也是阻塞方法,读取用户发来的名字
  84. name=dataInputStream.readUTF();
  85. } catch (IOException e1) {
  86. closeSocket(dataInputStream);
  87. }
  88. //循环列表,除了自己,其他服务Scoket全部转发消息
  89. for(ClientSocket clientSocket:clientSockets) {
  90. if(clientSocket!=this) {
  91. try {
  92. clientSocket.dataOutputStream.writeUTF("欢迎"+name+"进入聊天室");
  93. clientSocket.dataOutputStream.flush();//清空缓存区,让缓存区的数据全部出来
  94. } catch (IOException e) {
  95. clientSocket.closeSocket(clientSocket.dataOutputStream);
  96. }
  97. }
  98. }
  99. //知道名字后就一直等待接收用户端发的消息,异常就关闭Scoket并跳出循环
  100. while(true) {
  101. String msg = null;
  102. //在读取到的消息前加上姓名
  103. try {
  104. msg = name+":"+dataInputStream.readUTF();
  105. } catch (IOException e) {
  106. closeSocket(dataInputStream);
  107. break;
  108. }
  109. //判断信息中是否@人了,如果@人了,其他每个服务Socket判断@的名字里是不是自己服务的用户名字;如果没有@,则全部转发
  110. if(msg.contains("@")) {
  111. for(ClientSocket clientSocket:clientSockets) {
  112. if(clientSocket!=this) {
  113. try {
  114. if(msg.contains("@"+clientSocket.name)) { //找到了是自己的用户端名字
  115. //将@名字替换为空,replace返回一个心得字符串
  116. clientSocket.dataOutputStream.writeUTF(msg.replace("@"+clientSocket.name, ""));
  117. clientSocket.dataOutputStream.flush();
  118. }
  119. } catch (IOException e) {
  120. clientSocket.closeSocket(clientSocket.dataOutputStream);
  121. }
  122. }
  123. }
  124. }
  125. else {
  126. for(ClientSocket clientSocket:clientSockets) {
  127. if(clientSocket!=this) {
  128. try {
  129. clientSocket.dataOutputStream.writeUTF(msg);
  130. clientSocket.dataOutputStream.flush();
  131. } catch (IOException e) {
  132. clientSocket.closeSocket(clientSocket.dataOutputStream);
  133. }
  134. }
  135. }
  136. }
  137. }
  138. }
  139. }
  140. }

2.思路二(服务端代码)

用户名不要写一样的名字,他是根据名字找套接字,当查找到这个名字就去哈希表找对应的套接字,如果名字相同,他只会找第一个与这个名字相同的套接字。

  1. package com.csi.siliaoTest;
  2. import java.io.Closeable;
  3. import java.io.DataInputStream;
  4. import java.io.DataOutputStream;
  5. import java.io.IOException;
  6. import java.net.InetAddress;
  7. import java.net.ServerSocket;
  8. import java.net.Socket;
  9. import java.util.ArrayList;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import com.csi.siliaoTest.OpenServer.ClientSocket;
  13. public class OpenServer2 {
  14. //一个哈希表存储用户名字和服务套接字
  15. HashMap<String, ClientSocket> map= new HashMap<>();
  16. //一个列表存储服务Socket
  17. List<ClientSocket> clientSockets =new ArrayList<>();
  18. //一个列表存储用户名字
  19. List<String> names = new ArrayList<>();
  20. ServerSocket serverSocket = null;
  21. //构造方法中创建服务器ServerSocket
  22. public OpenServer2() {
  23. try {
  24. //因为本代码是在云服务器运行,直接用InetAddress.getLocalHost()方法绑定云服务器的IP地址及端口号
  25. serverSocket = new ServerSocket(7777, 50, InetAddress.getLocalHost());
  26. System.out.println("----------服务器----------");
  27. } catch (IOException e) {
  28. //发生异常。调用自己写的Utils类关闭服务器
  29. Utils.close(serverSocket);
  30. }
  31. //ServerSocket绑定成功,开始等待用户接入
  32. if(serverSocket.isBound())
  33. acceptClient();
  34. }
  35. //等待用户,并为他生成服务Socket
  36. private void acceptClient(){
  37. //while循环让服务器一直可以接入用户,一个用户接入服务器,服务器就生成一个为该用户服务的Socket
  38. while(true) {
  39. Socket socket=null;
  40. try {
  41. //ServerScoket的accept()方法是一个阻塞方法,他会在这里等用户接入,直到有用户接入,才会运行下面的代码
  42. socket=serverSocket.accept();
  43. System.out.println("一个用户接入.....");
  44. //用ClientSocket类包装Socket类,ClientSocket类是自己写的内部线程类,该类实现了接收和转发用户消息
  45. ClientSocket clientSocket=new ClientSocket(socket);
  46. //开启服务线程
  47. clientSocket.start();
  48. //将这个包装了服务Socket的对象添加进列表
  49. clientSockets.add(clientSocket);
  50. } catch (IOException e) {
  51. Utils.close(socket);
  52. }
  53. }
  54. }
  55. public static void main(String[] args) {
  56. //执行服务器代码
  57. new OpenServer2();
  58. }
  59. class ClientSocket extends Thread{
  60. Socket socket=null;
  61. DataInputStream dataInputStream=null;
  62. DataOutputStream dataOutputStream=null;
  63. String name = null;
  64. public ClientSocket(Socket socket) {
  65. this.socket = socket;
  66. //包装服务Socket的输入输出流,异常就调用closeScoket()方法
  67. try {
  68. dataInputStream=new DataInputStream(socket.getInputStream());
  69. } catch (IOException e) {
  70. closeSocket(dataInputStream,socket);
  71. }
  72. try {
  73. dataOutputStream=new DataOutputStream(socket.getOutputStream());
  74. } catch (IOException e) {
  75. closeSocket(dataOutputStream,socket);
  76. }
  77. }
  78. //上面的代码我们关闭的都是装饰流,因为关闭装饰流会将内部流也关闭,Socket也会因此关闭,同时我们也要将列表里对应Socket的删除
  79. public void closeSocket(Closeable...closeables) {
  80. Utils.close(closeables);
  81. System.out.println("一位用户退出");
  82. clientSockets.remove(this);
  83. names.remove(name);
  84. map.remove(name);
  85. }
  86. @Override
  87. public void run() {
  88. //读取用户姓名,并让列表所有用户转发欢迎信息
  89. //数据流DataIn/OutputStream的readUTF()和writeUTF(String data)要一起用,是将数据以UTF-8的编码方式发出或者接收
  90. try {
  91. //readUTF()方法也是阻塞方法,读取用户发来的名字并添加哈希表和列表
  92. name=dataInputStream.readUTF();
  93. names.add(name);
  94. map.put(name, this);
  95. } catch (IOException e1) {
  96. closeSocket(dataInputStream);
  97. }
  98. //循环列表,除了自己,其他服务Socket全部转发消息
  99. for(ClientSocket clientSocket:clientSockets) {
  100. if(clientSocket!=this) {
  101. try {
  102. clientSocket.dataOutputStream.writeUTF("欢迎"+name+"进入聊天室");
  103. clientSocket.dataOutputStream.flush();//清空缓存区,让缓存区的数据全部出来
  104. } catch (IOException e) {
  105. clientSocket.closeSocket(clientSocket.dataOutputStream);
  106. }
  107. }
  108. }
  109. //知道名字后就一直等待接收用户端发的消息,异常就关闭Scoket并跳出循环
  110. while(true) {
  111. String msg = null;
  112. //在读取到的消息前加上姓名
  113. try {
  114. msg = name+":"+dataInputStream.readUTF();
  115. } catch (IOException e) {
  116. closeSocket(dataInputStream);
  117. break;
  118. }
  119. //判断信息中是否@人了,如果@人了,查找是否有@的这个名字的用户
  120. if(msg.contains("@")) {
  121. ClientSocket clientSocket = null;
  122. for(String aname: names) {
  123. if (msg.contains("@"+aname)) {
  124. try {clientSocket =map.get(aname);
  125. clientSocket.dataOutputStream.writeUTF(msg.replace("@"+aname, ""));
  126. clientSocket.dataOutputStream.flush();
  127. } catch (IOException e) {
  128. clientSocket.closeSocket(clientSocket.dataOutputStream);
  129. }
  130. }
  131. }
  132. }
  133. else {
  134. for(ClientSocket clientSocket:clientSockets) {
  135. if(clientSocket!=this) {
  136. try {
  137. clientSocket.dataOutputStream.writeUTF(msg);
  138. clientSocket.dataOutputStream.flush();
  139. } catch (IOException e) {
  140. clientSocket.closeSocket(clientSocket.dataOutputStream);
  141. }
  142. }
  143. }
  144. }
  145. }
  146. }
  147. }
  148. }

注意:不要写一样的用户名字,他是在哈希表里根据名字找套接字,如果名字相同,他只会找第一个与这个名字相同的套接字。如果写一样得名字就只有一个用户会收到@的信息,并且会收到好几条(有几个重名就有几条)。如下图,我写了两个张三。


三、运行效果

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/178637
推荐阅读
相关标签
  

闽ICP备14008679号