当前位置:   article > 正文

JavaChatRoom_javachatroomserver

javachatroomserver

项目名称:  JavaChatRoom

项目功能:用户登录、用户下线、群聊、私聊

涉及技术:

              1.网络编程(JavaSocket API)

              2.Java多线程技术

              3.使用C/S架构

项目主体

               服务器

               1.使用ServerSocket来建立起一个连接,public ServerSocket(int port): 此方法默认绑定本地ip地址 127.0.0.1 及指定端口号

               2.建立连接accept:

                 public Socket accept(): 等待客户端连接,线程阻塞,当有客户端连接时,返回客户端socket

               3.服务器与客户端通信

                 当客户端与服务器建立起连接后,通过输入输出流(客户端socket)来进行通信

                 对于服务器端,获取客户端输入流,读取客户端发来的信息

                 public InputStream getInputStream()

                 获取客户端输出流,向客户端发送信息

                 public OutputStream getOutputStream()

                 服务器端功能:

                 注册用户:userName: name

                 用户群聊:G:聊天内容

                 用户私聊:P:test-聊天内容

                 用户退出:byebye

                客户端:

                   1.Socket类:

                   使用Socket方法: public Socket(String host,int port):绑定指定域名,端口号的服务器,只要不报错就建立连接

                   2.客户端与服务器通信:

                   获取客户端输入流,读取服务器发来的信息

                   public InPutStream  getInputStream()

                   获取客户端输出流,向服务器发送信息

                   public OutputStream getOutputSteam()

主体代码实现

分为单线程多线程版本:

单线程版本

由于是单线程版本,当没有客户端连接时,服务器线程一直处于阻塞状态,直到有客户端连接时,才会返回客户端socket

服务端代码

  1. public class SingleThreadServer {
  2. public static void main(String[] args) {
  3. try {
  4. // 建立服务端ServerScoket 并绑定本地 6666 端口号
  5. ServerSocket serverSocket = new ServerSocket(6666);
  6. // 等待客户端连接
  7. System.out.println("等待客户端连接ing...");
  8. // 服务器线程一直阻塞,直到有客户端连接,返回客户端连接Socket
  9. Socket client = serverSocket.accept();
  10. System.out.println("有客户端连接,客户端端口号为"+client.getPort());
  11. //获取客户端输出流,向客户端输出信息 自动刷新
  12. PrintStream out = new PrintStream(client.getOutputStream(),true ,"UTF-8");
  13. //获取客户端输入流,读取客户端信息
  14. Scanner in = new Scanner(client.getInputStream());
  15. if(in.hasNextLine()){
  16. //读取一整行输入
  17. System.out.println("客户端发来的信息为:"+in.nextLine());
  18. }
  19. out.println("Server : Hello sir, I am Jarvis,may I help you?");
  20. //关闭流
  21. in.close();
  22. out.close();
  23. serverSocket.close();
  24. } catch (IOException e) {
  25. System.err.println("服务器建立连接失败,异常为 "+e);
  26. }
  27. }
  28. }

服务端界面

 

客户端代码

1.客户端建立与服务器的连接,绑定本地ip地址和 指定端口号 6666

2.由于是单线程,服务器是先得到客户端信息,才能向客户端发送消息,为了防止线程阻塞,客户端先向服务器发送信息,再获取服务器端信息

  1. public class SingleThreadClient {
  2. public static void main(String[] args) {
  3. try {
  4. // 建立客户端Socket并绑定服务器
  5. Socket client = new Socket("127.0.0.1",6666);
  6. // 获取输入、输出流与服务器通信
  7. // 获取输出流,向服务器发送信息
  8. PrintStream out = new PrintStream(client.getOutputStream(),
  9. true,"UTF-8");
  10. out.println("Hi,I am Client");
  11. //获取输入流,读取服务器发送的信息
  12. Scanner in = new Scanner(client.getInputStream());
  13. if(in.hasNextLine()){
  14. System.out.println("服务器发来的信息为 "+in.nextLine());
  15. }
  16. in.close();
  17. out.close();
  18. client.close();
  19. } catch (Exception e) {
  20. System.err.println("客户端出现异常 :"+e);
  21. }
  22. }
  23. }

客户端界面

 

多线程版本

多线程服务器

1.使用固定大小线程池实现多线程,这里使用的线程池大小为20

2.在这里使用内部类实现客户端的请求,每当有新的客户端请求,就新建线程处理这个请求

直到线程池满就关闭线程池,关闭服务器

3.在服务器端中,使用concurrentHashMap来存储所有连接到服务器的客户端信息

4.由于每个平台中的换行符不同,识别windows下的换行符,将其替换为 " "

  1. // 识别windows下换行符,将 \r 替换为 ""
  2. // pattern为模式,matcher为匹配,匹配后做一个替换
  3. Pattern pattern = Pattern.compile("\r");
  4. Matcher matcher = pattern.matcher(str);
  5. matcher.replaceAll("");

5.四大功能实现

  1. // 获取客户端输入流,读取客户端发来的信息
  2. Scanner in = new Scanner(client.getInputStream());

根据这行代码读取客户端发来的信息

  1. // 一行一行读取客户端信息
  2. if(in.hasNextLine()){
  3. str = in.nextLine();

 1.注册功能实现

          如果识别到 输入信息中有 "userName",就说明有用户注册,使用 split()方法进行字符串分割,将用户名信息分割出来,然后使用 userRegister()方法,将用户信息,及socket保存在 map中,并且打印聊天室中人数。

  1. if(str.startsWith("userName")){
  2. // userName:test
  3. String userName = str.split("\\:")[1];
  4. userRegister(userName,client);
  5. continue;
  6. }
  7. // 注册方法
  8. private static void userRegister(String userName,Socket socket){
  9. System.out.println("用户"+userName+"上线了!");
  10. // 将用户保存在map中
  11. clientMap.put(userName,socket);
  12. System.out.println("当前聊天室中一共有"+clientMap.size()+"人");
  13. // 告知客户端注册成功
  14. try {
  15. // 获取每个Socket的输出流
  16. PrintStream out = new PrintStream(socket.getOutputStream());
  17. out.println("用户注册成功!");
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. }

 用户 one two three 都上线,并且聊天室中人数随之更新

2.群聊功能实现

 如果识别到输入信息中有 "G",就说明是群聊信息,使用 split() 方法,分割出有用信息

再使用 groupChat(msg) 方法,遍历取出每个soceket,并且获取每个socket的输出流,向每个客户端发送群聊消息

  1. // 群聊方法
  2. private static void GroupChat(String msg){
  3. Set<Map.Entry<String,Socket>> clientSet =
  4. clientMap.entrySet();
  5. for(Map.Entry<String,Socket> entry : clientSet){
  6. // 遍历取出每个Socket
  7. Socket socket = entry.getValue();
  8. // 获取每个Socket 的输出流
  9. try {
  10. PrintStream out = new PrintStream(socket.getOutputStream());
  11. out.println("群聊消息为 "+msg);
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }

3.私聊方法实现

如果输入信息中有" P ",就说明是私聊方法,获取用户名,聊天信息,使用PrivatedChat()方法,传入用户名,信息

然后根据用户名获取指定的socekt,然后获取该socket的输出流,使用 socket.getOutputStream() 向用户发送信息

  1. // 私聊方法
  2. private static void PrivateChat(String userName,String msg){
  3. // 根据用户名获取指定Socket
  4. Socket socket = clientMap.get(userName);
  5. try {
  6. // 获得指定Socket输出流
  7. PrintStream out = new PrintStream(socket.getOutputStream());
  8. out.println("私聊消息 "+msg);
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. }
  12. }

4.用户退出实现

如果输入的信息中包含 "byebye", 那么遍历map中的key,找到与client相等的 key,移除掉 username ,并同步聊天室中剩余人数

  1. // 客户端退出
  2. if(str.contains("byebye")){
  3. // 遍历Map,获取userName
  4. String userName = "";
  5. // 获取所有的方法
  6. for(String key : clientMap.keySet()){
  7. // 遍历 Map中的key,找到与client相等的value
  8. if(clientMap.get(key).equals(client)){
  9. userName = key;
  10. }
  11. }
  12. System.out.println("用户"+userName+"下线了...");
  13. clientMap.remove(userName);
  14. System.out.println("当前聊天室一共 "+clientMap.size()+"人");
  15. continue;
  16. }

当有客户端下线时,服务器显示 具体哪个客户端下线,并显示聊天室中还有几人 

多线程客户端

 1.socket 绑定服务器 ip 地址及 端口号

 2.客户端使用全双工,一个读线程,一个写线程,实现信息的收发

客户端读写线程代码

  1. public class MultiThreadClient {
  2. public static void main(String[] args) {
  3. try {
  4. Socket client = new Socket("127.0.0.1",6666);
  5. Thread readThread = new Thread(new ReadFromServer(client));
  6. Thread writeThread = new Thread(new WriteToServer(client));
  7. readThread.start();
  8. writeThread.start();
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. }

读线程实现

    获取客户端输入流,使用 Scanner in = new Scanner(client.getInputStream()) 读取服务器发来的信息,

  1. class ReadFromServer implements Runnable{
  2. private Socket client;
  3. public ReadFromServer(Socket client){
  4. this.client = client;
  5. }
  6. @Override
  7. public void run() {
  8. try {
  9. // 获取客户端输入流,读取服务器发送的信息
  10. Scanner in = new Scanner(client.getInputStream());
  11. while(true){
  12. // 若client已经被关闭,此处自动退出了
  13. if(in.hasNextLine()){
  14. System.out.println("从服务器发来的信息为 "+in.nextLine());
  15. }
  16. }
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

写线程实现:

   //  获取键盘输入流,读取用户从键盘发送的信息

    Scanner in = new Scanner(system.in)

   //  获取客户端输出流,将用户键入的信息发送给服务器

   PrintStream out = new PrintStream(client.getOutputStream())

  当用户的输入信息含有 "byebye",关闭键盘输入流,输出流,及客户端

  1. class WriteToServer implements Runnable{
  2. private Socket client;
  3. public WriteToServer(Socket client){
  4. this.client = client;
  5. }
  6. @Override
  7. public void run() {
  8. try {
  9. // 获取键盘输入流,读取用户从键盘发来的信息
  10. Scanner scanner = new Scanner(System.in);
  11. String string = "";
  12. // 获取客户端输入流,将用户键入的信息发送给服务器
  13. PrintStream out = new PrintStream(client.getOutputStream(),
  14. true,"UTF-8");
  15. while(true){
  16. System.out.println("请输入向服务器发送的信息");
  17. if(scanner.hasNextLine()){
  18. string = scanner.nextLine();
  19. out.println(string);
  20. }
  21. //设置退出标志
  22. if(string.contains("byebye")){
  23. System.out.println("客户端退出,不聊了");
  24. scanner.close();
  25. out.close();
  26. client.close();
  27. break;
  28. }
  29. }
  30. } catch (IOException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. }

客户端注册界面

客户端one,two,three 都进行注册

 客户端群聊界面

客户端one发起群聊,所有客户端都可以接收到此消息,发送信息为:testtest!

 客户端 two 接收到 one发送的消息

 

客户端 three 接收到 one发送的消息

 客户端私聊界面

three 向 one 送消息

one 接收到消息

由于是私聊,two 没有接收到消息

 

客户端退出界面: 

当 one 发送信息中含有 byebye时,客户端 one 下线

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

闽ICP备14008679号