32. 聊天程序

两个工程------客户端    服务端

1.总览

1.1客户端工程

 

1.2 服务端工程

 

2.客户端工程代码

2.1 ClientMain

package com.weiwei.qq.main;



import com.weiwei.qqclient.view.QQView;



public class ClientMain {



   public static void main(String[] args) {

      new QQView().mainMenu();



   }



}

2.2 ClientConnectionServerThread

package com.weiwei.qqclient.service;



import java.io.File;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.net.Socket;



import com.weiwei.qqcommon.Message;

import com.weiwei.qqcommon.MessageType;



public class ClientConnectionServerThread extends Thread{

   //该线程必须持有Socket

   private Socket socket;



   //构造方法目的是可以接收一个传过来的socket,赋给当前类的socket

   public ClientConnectionServerThread(Socket socket) {

      this.socket = socket;

   }



   public Socket getSocket() {

      return socket;

   }



   public void setSocket(Socket socket) {

      this.socket = socket;

   }



   @Override

   public void run() {

      //因为线程需要在后台和服务器保持通信,因此while循环

      while(true){

          //客户端的线程是负责读取从服务器端发来的消息

          try {

             ObjectInputStream objin = new ObjectInputStream(socket.getInputStream());

             Message msg=(Message)objin.readObject();//如果服务器没有发message过来,线程会阻塞在这里

             //判断客户端返回的msg类型

            

             /**

              * 显示在线用户列表

              */

          if(msg.getMesType().equals(MessageType.MESSAGE_RETURN_ONLINE)){

                //返回的是在线用户数据,取出并显示

                   //规定在线用户列表是仅显示用户id  空格隔开

                String[] onlineusers = msg.getContent().split(",");

                System.out.println("======当前用户在线人数"+onlineusers.length+"人======");

                for(int i=0;i<onlineusers.length;i++){

                   System.out.println("用户:"+onlineusers[i]);

                }

            

             }

            

             /**

              *

              * 显示私发消息

              */

             if(msg.getMesType().equals(MessageType.MESSAGE_COMM_MES)){

                //把服务器转发来的消息显示出来

                System.out.println("\n"+msg.getSender()+"对你说:"+msg.getContent()+"\t\t"+msg.getSendTime());

             }

            

            

             /**

              *显示群发消息

              *

              *

              */

             if(msg.getMesType().equals(MessageType.MESSAGE_TOALL)){

                //把服务器转发来的消息显示出来

                System.out.println("\n"+msg.getSender()+"对大家说:"+msg.getContent()+"\t\t"+msg.getSendTime());

               

               

               

             }

            

             /**

              *

              * 显示文件

              *

              */

             if(msg.getMesType().equals(MessageType.MESSAGE_FILE)){

                //把服务器转发来的消息显示出来

                System.out.println("\n"+msg.getSender()+"给你发来了一个文件("+msg.getSrc()

                +")  到我的电脑的目录("+msg.getDestination()+") \t\t"+msg.getSendTime());

               

                //取出message中的文件字节数组,通过输出流写入到磁盘

                FileOutputStream fileout=new FileOutputStream(new File(msg.getDestination()));

                fileout.write(msg.getFileBytes());

                fileout.close();

                System.out.println("\n 自动保存文件成功");

               

               

             }

            

               

          } catch (Exception e) {

             e.printStackTrace();

          }

      }

   }

}

2.3 FileService

package com.weiwei.qqclient.service;



import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.ObjectOutputStream;

import java.io.OutputStream;

import java.text.SimpleDateFormat;

import java.util.Date;



import com.weiwei.qqcommon.Message;

import com.weiwei.qqcommon.MessageType;



/**

 * 该类完成文件传输

 *

 */

public class FileService {



   /**

    *

    * src 源文件 destination 传输到对方哪个目录 senderid 发送用户id getterid 接收用户id

    */

   public void sendFileToOne(String src, String destination, String senderid, String getterid) {

      //创建message对象

      Message message = new Message();

      message.setMesType(MessageType.MESSAGE_FILE);

      message.setSender(senderid);

      message.setGetter(getterid);

      message.setDestination(destination);

      message.setSrc(src);

      message.setSendTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));// 发送时间

     

      //读取文件

      File file = new File(src);

      FileInputStream filein=null;

      byte[] fileBytes=new byte[(int)file.length()];

     

      try {

          filein=new FileInputStream(file);

          filein.read(fileBytes);//将src文件读入到程序的字节数组

          //将文件对应的字节数组设置message

          message.setFileBytes(fileBytes);  

         

      } catch (Exception e) {

          e.printStackTrace();

      }finally{

          //关闭

          try {

             filein.close();

          } catch (Exception e) {

             e.printStackTrace();

          }

      }

     

      //提示信息

      System.out.println("\n"+senderid+"给"+getterid+"发送文件:"+src+"到对方的电脑的目录"+destination);

     

      //发送message

      try {

          OutputStream outputStream = ManageClientConnServerThread.getThread(senderid).getSocket()

                .getOutputStream();

          ObjectOutputStream objectout = new ObjectOutputStream(outputStream);

          objectout.writeObject(message);

      } catch (Exception e) {

          e.printStackTrace();

      }



   }



  

}

2.4 ManageClientConnServerThread

package com.weiwei.qqclient.service;



import java.util.HashMap;



/**

 *

 * 管理客户端连接到服务器端的线程的类

 *

 */

public class ManageClientConnServerThread {

   //把多个线程放入到一个HashMap集合,key就是用户id,value就是线程

   private static HashMap<String,ClientConnectionServerThread> hm=new HashMap<>();

  

   //将某个线程加入到hashmap

   public static void addThread(String userid,ClientConnectionServerThread thread){

      hm.put(userid, thread);

   }

  

   //通过userid可以得到对应线程

   public static ClientConnectionServerThread getThread(String userid){

      return hm.get(userid);

   }

}

2.5 UserClientService

package com.weiwei.qqclient.service;



import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.OutputStream;

import java.net.InetAddress;

import java.net.Socket;

import java.text.SimpleDateFormat;

import java.util.Date;



import com.weiwei.qqcommon.Message;

import com.weiwei.qqcommon.MessageType;

import com.weiwei.qqcommon.User;

import com.weiwei.utils.Utils;



/**

 * 完成用户登录验证功能

 *

 *

 */

public class UserClientService {

   private User user = new User();

   private Socket socket;



   // 根据userid和password到服务器验证该用户是否合法

   public boolean checkUser(String userid, String pwd) {

      user.setUserid(userid);

      user.setPassword(pwd);

      boolean b = false;

      try {

          // 连接到服务器,发送user对象

          socket = new Socket(InetAddress.getLocalHost(), 9999);

          // 得到socket输出流

          OutputStream outputStream = socket.getOutputStream();

          // 创建可以发送对象的输出流

          ObjectOutputStream objout = new ObjectOutputStream(outputStream);

          // 发送user对象

          objout.writeObject(user);



          // 读取从服务端回复的message对象

          ObjectInputStream objin = new ObjectInputStream(socket.getInputStream());

          Message msg = (Message) objin.readObject();



          if (msg.getMesType().equals(MessageType.MESSAGE_LOGIN_SUCCEED)) {// 登录成功

             // 创建一个和服务器保持通信的线程,需要创建一个类ClientConnectionServerThread

             ClientConnectionServerThread cnServerThread = new ClientConnectionServerThread(socket);

             cnServerThread.start();

             // 为了客户端的扩展性,将线程放入到集合管理

             ManageClientConnServerThread.addThread(userid, cnServerThread);

             b = true;

          } else {

             // 如果登录失败,就不能启动和服务器通信的线程,关闭socket

             socket.close();

          }

      } catch (Exception e) {

          e.printStackTrace();

      }

      return b;

   }



   /**

    *

    * 向服务器端请求在线用户列表

    *

    */

   public void onlinePeople() {

      // 向服务器发送一个message,请求在线用户列表

      Message msg = new Message();

      msg.setMesType(MessageType.MESSAGE_GET_ONLINE);

      msg.setSender(user.getUserid());

      // 发送给服务器

      // 得到当前线程的socket对应的ObjectOutputStream对象

      ClientConnectionServerThread thread = ManageClientConnServerThread.getThread(user.getUserid());// 拿到当前线程

      try {

          // 得到当前线程的socket的OutputStream对象

          OutputStream outputStream = thread.getSocket().getOutputStream();

          // 得到发送对象的输出流

          ObjectOutputStream objectout = new ObjectOutputStream(outputStream);

          // 发送给服务器

          objectout.writeObject(msg);



      } catch (Exception e) {

          e.printStackTrace();

      }



   }



   /**

    * 退出客户端,并给服务端发送一个退出系统的message对象

    */

   public void exit() {

      Message message = new Message();

      message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);

      message.setSender(user.getUserid());



      // 发送message

      try {

          ObjectOutputStream objout = new ObjectOutputStream(socket.getOutputStream());

          objout.writeObject(message);

          System.out.println(user.getUserid() + "已退出系统");

          System.exit(0);

      } catch (Exception e) {

          e.printStackTrace();

      }

   }



   /**

    * 私聊消息

    *

    *

    */

   public void siliao() {

      // 得到用户聊天的另一用户id

      String getterid = Utils.getString("请输入想聊天的用户(在线):");

      // 得到用户输入的信息

      String chatinfo = Utils.getString("请输入聊天内容:");

      Message message = new Message();

      message.setMesType(MessageType.MESSAGE_COMM_MES);

      message.setSender(user.getUserid());

      message.setGetter(getterid);

      message.setContent(chatinfo);

      message.setSendTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));// 发送时间

      System.out.println(user.getUserid() + "对" + getterid + "说:" + chatinfo);



      // 发送message给服务端

      try {

          OutputStream outputStream = ManageClientConnServerThread.getThread(user.getUserid()).getSocket()

                .getOutputStream();

          ObjectOutputStream objectout = new ObjectOutputStream(outputStream);

          objectout.writeObject(message);



      } catch (Exception e) {

          e.printStackTrace();

      }

   }



  

   /**

    *

    * 群发消息

    *

    *

    */

   public void qunfa() {

      // 得到用户输入的信息

      String chatinfo = Utils.getString("请输入群发内容:");

      Message message = new Message();

      message.setMesType(MessageType.MESSAGE_TOALL);

      message.setSender(user.getUserid());

      message.setContent(chatinfo);

      message.setSendTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));// 发送时间

      System.out.println(user.getUserid() + "对所有人说:" + chatinfo);



      // 发送message给服务端

      try {

          OutputStream outputStream = ManageClientConnServerThread.getThread(user.getUserid()).getSocket()

                .getOutputStream();

          ObjectOutputStream objectout = new ObjectOutputStream(outputStream);

          objectout.writeObject(message);



      } catch (Exception e) {

          e.printStackTrace();

      }



   }

  

  

   /**

    * 发送文件

    *

    */

   public void sendfilm() {

      String src = Utils.getString("请输入您要发送的文件路径(格式:F:\\a\\...):");

      String getterid = Utils.getString("请输入您要发送的人(在线):");

      String destination = Utils.getString("请输入对方接收保存的路径:");

      FileService fileService = new FileService();

      fileService.sendFileToOne(src, destination, user.getUserid(), getterid);

   }



}

2.6 QQView

package com.weiwei.qqclient.view;



import com.weiwei.qqclient.service.UserClientService;

import com.weiwei.utils.Utils;



/**

 *

 * 客户端菜单界面

 *

 */

public class QQView {



   /**

    * 显示主菜单

    */

   private boolean loop = true;

   private int number = 0;

   private UserClientService userclientservice=new UserClientService();

  

   public void mainMenu() {

      while (loop) {

          System.out.println("=======欢迎使用通讯聊天系统=======");

          System.out.println("\t 1 登录系统");

          System.out.println("\t 9 退出系统");

          number = Utils.getNumber("请输入菜单按键:");// 得到一个数字

          // 根据输入的数字处理不同的逻辑

          switch (number) {

          case 1:

             String userid = Utils.getString("请输入用户名:");//得到一个用户名

             String password = Utils.getString("请输入密码:");//得到一个密码

             //需要服务端验证密码是否正确

             boolean checkUser = userclientservice.checkUser(userid, password);

             if(checkUser){

                //登录成功

                //进入二级菜单

                while(loop){

                   System.out.println("=======欢迎用户("+userid+")使用聊天系统=======");

                   System.out.println("\t 1 显示在线用户列表");

                   System.out.println("\t 2 私发消息");

                   System.out.println("\t 3 群发消息");

                   System.out.println("\t 4 发送文件");

                   System.out.println("\t 9 退出");

                   number= Utils.getNumber("请输入菜单按键:");

                   switch(number){

                   case 1:

                      //显示在线用户列表

                      userclientservice.onlinePeople();

                      break;

                   case 2:

                      //私聊

                      userclientservice.siliao();             

                      break;

                   case 3:

                      //群发消息

                      userclientservice.qunfa();

                      break;

                   case 4:

                      //发送文件

                      userclientservice.sendfilm();

                      break;

                   case 9:

                      //退出

                      userclientservice.exit();

                      loop=false;

                      break;

                   default:

                      System.out.println("您输入有误,请重新输入菜单数字按钮");

                      break;

                   }

                }

             }else{//登录失败

                System.out.println("用户名或密码不正确,请重新登录");

             }

            

            

             break;

          case 9:

             System.out.println("您已退出聊天系统");

             loop=false;

             break;

          default:

             System.out.println("您输入有误,请重新输入菜单数字按钮");

             break;

          } 

      }

   }

}

2.7 Message

package com.weiwei.qqcommon;



import java.io.Serializable;



/**

 *

 * 表示客户端和服务器通信时的消息对象

 *

 */

public class Message implements Serializable{

   private static final long serialVersionUID=1L;//增强序列化兼容性

  

   private String sender;//发送者

   private String getter;//接受者

   private String content;//消息内容

   private String sendTime;//发送时间

   private String mesType;//消息类型[可以在接口定义消息类型]

  

   //扩展文件消息

   private byte[] fileBytes;

   private int filelen=0;

   private String destination;//将文件传输到哪里

   private String src;//源文件路径

  

  

  

   public String getSender() {

      return sender;

   }

   public void setSender(String sender) {

      this.sender = sender;

   }

   public String getGetter() {

      return getter;

   }

   public void setGetter(String getter) {

      this.getter = getter;

   }

   public String getContent() {

      return content;

   }

   public void setContent(String content) {

      this.content = content;

   }

   public String getSendTime() {

      return sendTime;

   }

   public void setSendTime(String sendTime) {

      this.sendTime = sendTime;

   }

   public String getMesType() {

      return mesType;

   }

   public void setMesType(String mesType) {

      this.mesType = mesType;

   }

   public static long getSerialversionuid() {

      return serialVersionUID;

   }

   public byte[] getFileBytes() {

      return fileBytes;

   }

   public void setFileBytes(byte[] fileBytes) {

      this.fileBytes = fileBytes;

   }

   public int getFilelen() {

      return filelen;

   }

   public void setFilelen(int filelen) {

      this.filelen = filelen;

   }

   public String getDestination() {

      return destination;

   }

   public void setDestination(String destination) {

      this.destination = destination;

   }

   public String getSrc() {

      return src;

   }

   public void setSrc(String src) {

      this.src = src;

   }

  

}

2.8 MessageType

package com.weiwei.qqcommon;



public interface MessageType {

   /*

    * 在接口中定义了一些常量

    * 不同的常量的值,表示不同的消息类型

    */

   String MESSAGE_LOGIN_SUCCEED="1";//表示登录成功

   String MESSAGE_LOGIN_FAIL="2";//表示登录失败

   String MESSAGE_COMM_MES="3";//普通信息包

   String MESSAGE_GET_ONLINE="4";//要求返回在线用户列表

   String MESSAGE_RETURN_ONLINE="5";//返回在线用户列表

   String MESSAGE_CLIENT_EXIT="6";//客户端请求退出

   String MESSAGE_TOALL="7";//群发消息

   String MESSAGE_FILE="8";//文件消息

}

2.9 User

package com.weiwei.qqcommon;



import java.io.Serializable;



/**

 *

 * 表示一个用户信息

 * 实现序列化

 */

public class User implements Serializable{

  

   private static final long serialVersionUID=1L;//增强序列化兼容性

  

   private String userid;//用户id

   private String password;//用户密码

  

   public User(){

     

   }

  

   public User(String userid, String password) {

      super();

      this.userid = userid;

      this.password = password;

   }



   public String getUserid() {

      return userid;

   }



   public void setUserid(String userid) {

      this.userid = userid;

   }



   public String getPassword() {

      return password;

   }



   public void setPassword(String password) {

      this.password = password;

   }

}

2.10 Utils

package com.weiwei.utils;



import java.util.Scanner;



public class Utils {

  

   /**

    * 键盘输入得到一个数字

    */

   public static int getNumber(String string) {

      int number=0;

      try{

      System.out.println(string);

      Scanner sc=new Scanner(System.in);

       number = sc.nextInt();

      //如果用户输入得不是数字,需要异常处理

      }catch(Exception e){

          System.out.println("您输入有误,请重新输入");

          getNumber(string);

      }

      return number;

   }

  

   /**

    * 键盘输入得到一个字符串

    */

   public static String getString(String string) {

      System.out.println(string);

      // 创建键盘录入对象

      String info="";

      Scanner sc = new Scanner(System.in);

      info = sc.nextLine();

      if(info.length()==0){

          System.out.println("输入不能为空,请重新输入:");

          getString(string);

          }

      return info;

   }

}

3.服务端工程

3.1 Message

package com.weiwei.qqcommon;



import java.io.Serializable;



/**

 *

 * 表示客户端和服务器通信时的消息对象

 *

 */

public class Message implements Serializable{

   private static final long serialVersionUID=1L;//增强序列化兼容性

  

   private String sender;//发送者

   private String getter;//接受者

   private String content;//消息内容

   private String sendTime;//发送时间

   private String mesType;//消息类型[可以在接口定义消息类型]

  

   //扩展文件消息

   private byte[] fileBytes;

   private int filelen=0;

   private String destination;//将文件传输到哪里

   private String src;//源文件路径

  

  

  

  

   public byte[] getFileBytes() {

      return fileBytes;

   }

   public void setFileBytes(byte[] fileBytes) {

      this.fileBytes = fileBytes;

   }

   public int getFilelen() {

      return filelen;

   }

   public void setFilelen(int filelen) {

      this.filelen = filelen;

   }

   public String getDestination() {

      return destination;

   }

   public void setDestination(String destination) {

      this.destination = destination;

   }

   public String getSrc() {

      return src;

   }

   public void setSrc(String src) {

      this.src = src;

   }

   public String getSender() {

      return sender;

   }

   public void setSender(String sender) {

      this.sender = sender;

   }

   public String getGetter() {

      return getter;

   }

   public void setGetter(String getter) {

      this.getter = getter;

   }

   public String getContent() {

      return content;

   }

   public void setContent(String content) {

      this.content = content;

   }

   public String getSendTime() {

      return sendTime;

   }

   public void setSendTime(String sendTime) {

      this.sendTime = sendTime;

   }

   public String getMesType() {

      return mesType;

   }

   public void setMesType(String mesType) {

      this.mesType = mesType;

   }

   public static long getSerialversionuid() {

      return serialVersionUID;

   }



}

3.2 MessageType

package com.weiwei.qqcommon;



public interface MessageType {

   /*

    * 在接口中定义了一些常量

    * 不同的常量的值,表示不同的消息类型

    */

   String MESSAGE_LOGIN_SUCCEED="1";//表示登录成功

   String MESSAGE_LOGIN_FAIL="2";//表示登录失败

   String MESSAGE_COMM_MES="3";//普通信息包

   String MESSAGE_GET_ONLINE="4";//要求返回在线用户列表

   String MESSAGE_RETURN_ONLINE="5";//返回在线用户列表

   String MESSAGE_CLIENT_EXIT="6";//客户端请求退出

   String MESSAGE_TOALL="7";//群发消息

   String MESSAGE_FILE="8";//文件消息

  

}

3.3 User

package com.weiwei.qqcommon;



import java.io.Serializable;



/**

 *

 * 表示一个用户信息

 * 实现序列化

 */

public class User implements Serializable{

  

   private static final long serialVersionUID=1L;//增强序列化兼容性

  

   private String userid;//用户id

   private String password;//用户密码

  

   public User(String userid, String password) {

      super();

      this.userid = userid;

      this.password = password;

   }



   public String getUserid() {

      return userid;

   }



   public void setUserid(String userid) {

      this.userid = userid;

   }



   public String getPassword() {

      return password;

   }



   public void setPassword(String password) {

      this.password = password;

   }

}

3.4 ManageServerConnClientThread

package com.weiwei.qqserver.service;



import java.util.Date;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Set;



/**

 *

 * 管理服务器与客户端通信的线程的类

 *

 */



public class ManageServerConnClientThread {

   //把多个线程放入到一个HashMap集合,key就是用户id,value就是线程

   private static HashMap<String,ServerConnectionClientThread> hm=new HashMap<>();

  

   //将某个线程加入到hashmap

   public static void addThread(String userid,ServerConnectionClientThread thread){

      hm.put(userid, thread);

   }

  

   //通过userid可以得到对应线程

   public static ServerConnectionClientThread getThread(String userid){

      return hm.get(userid);

   }

  

  

   /**

    *

    * 返回在线用户列表

    *

    *

    */

   public static String getOnlineUser(){

      //遍历hashmap集合的key,使用迭代器方法

      Set<String> keySet = hm.keySet();//得到键的集合

      Iterator<String> iterator = keySet.iterator();//得到迭代器

      String onlineUserlist="";

      while(iterator.hasNext()){

          onlineUserlist=onlineUserlist+iterator.next()+",";

          if(!iterator.hasNext()){

             onlineUserlist=onlineUserlist.substring(0, onlineUserlist.lastIndexOf(","));

          }

      }

      return onlineUserlist;

     

   }

  

   /**

    * 从集合中移除某个线程对象

    */

   public static void remove(String userid){

      hm.remove(userid);

   }

  

   /**

    * 返回一个hm

    */

   public static HashMap<String,ServerConnectionClientThread> getHm(){

      return hm;

   }

}

3.5 QQServerService

package com.weiwei.qqserver.service;



import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.concurrent.ConcurrentHashMap;



import com.weiwei.qqcommon.Message;

import com.weiwei.qqcommon.MessageType;

import com.weiwei.qqcommon.User;



/**

 * 服务器在监听9999,等待客户端连接,并保持通信

 *

 *

 */

public class QQServerService {

   private ServerSocket serversocket = null;



   // 创建一个集合,存放多个用户,如果是这些用户登录,就认为我合法

      //ConcurrentHashMap专门处理并发,线程安全,HashMap多线程下不安全

   private static ConcurrentHashMap<String, User> validUsers = new ConcurrentHashMap<String, User>();



   static {// 在静态代码块初始化validUsers

      validUsers.put("张三", new User("张三", "123456"));

      validUsers.put("李四", new User("李四", "123456"));

      validUsers.put("王五", new User("王五", "123456"));

      validUsers.put("赵六", new User("赵六", "123456"));

      validUsers.put("鲁班", new User("鲁班", "123456"));

      validUsers.put("小乔", new User("小乔", "123456"));

      validUsers.put("貂蝉", new User("貂蝉", "123456"));

   }



   public QQServerService(){

      //注意:端口可以写在配置文件

      try{

          System.out.println("服务器在9999端口监听.....");

          serversocket=new ServerSocket(9999);

         

          while(true){//当和某个客户端连接后,会继续监听

             //接收到客户端的socket

             Socket socket = serversocket.accept();//如果没有客户端连接,则会阻塞在这

             ObjectInputStream objin = new ObjectInputStream(socket.getInputStream());

             ObjectOutputStream objout = new ObjectOutputStream(socket.getOutputStream());

             User user=(User)objin.readObject();

            

             //验证用户

             boolean checkUser = checkUser(user.getUserid(),user.getPassword());

             //创建一个Message对象,准备回复客户端

             Message msg=new Message();

             if(checkUser){//登录成功

                msg.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);

                //将message对象回复给客户端

                objout.writeObject(msg);

                //创建一个线程,和客户端保持通信

                ServerConnectionClientThread connectionClientThread = new ServerConnectionClientThread(socket,user.getUserid());

                //启动该线程

                connectionClientThread.start();

                //把该线程对象放入到一个集合中,进行管理

                ManageServerConnClientThread.addThread(user.getUserid(), connectionClientThread);

             }else{//登录失败

                System.out.println("用户id="+user.getUserid()+" password="+user.getPassword()+"验证失败");

               

                msg.setMesType(MessageType.MESSAGE_LOGIN_FAIL);

                objout.writeObject(msg);

                //登录失败,关闭socket

                socket.close();

             }

            

          }

      }catch(Exception e){

          e.printStackTrace();

      }finally{

          //如果服务器退出了while循环,因此需要关闭serversocket

          try {

             serversocket.close();

          } catch (IOException e) {

             e.printStackTrace();

          }

      }

   }

  

  



   /**

    * 验证用户是否有效

    */

   private boolean checkUser(String userid, String password) {

      User user = validUsers.get(userid);

      if (user == null) {// 说明不存在该用户,用户名错误

          return false;

      }

      if(!user.getPassword().equals(password)){//密码错误

          return false;

      }else{

          return true;

      }

   }



}

3.6 ServerConnectionClientThread

package com.weiwei.qqserver.service;



import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.OutputStream;

import java.net.Socket;

import java.util.HashMap;

import java.util.Iterator;



import com.weiwei.qqcommon.Message;

import com.weiwei.qqcommon.MessageType;



public class ServerConnectionClientThread extends Thread {

   // 该线程必须持有Socket

   private Socket socket;

   private String userid;



   // 构造方法目的是可以接收一个传过来的socket,赋给当前类的socket

   public ServerConnectionClientThread(Socket socket, String userid) {

      this.socket = socket;

      this.userid = userid;

   }



   public Socket getsocket() {

      return socket;

   }



   @Override

   public void run() {

      // 因为线程需要在后台和客户端保持通信,因此while循环

      while (true) {

          // 服务端的线程是负责读取从客户端发来的消息

          try {

             System.out.println("服务端和客户端:" + userid + "正在保持通讯....");

             ObjectInputStream objin = new ObjectInputStream(socket.getInputStream());

             Message msg = (Message) objin.readObject();// 读取客户端发来的message对象

             // 判断客户端发来msg类型



             /**

              * 服务器返回在线用户列表

              */

             if (msg.getMesType().equals(MessageType.MESSAGE_GET_ONLINE)) {

                // 客户端要在线用户列表

                /*

                 * 在线用户列表形式 userid ,隔开

                 */

                System.out.println(msg.getSender() + "请求在线用户列表");

                String onlineUser = ManageServerConnClientThread.getOnlineUser();

                // 返回message对象给客户端

                // 构建一个message对象

                Message message2 = new Message();

                message2.setMesType(MessageType.MESSAGE_RETURN_ONLINE);

                message2.setContent(onlineUser);

                message2.setGetter(msg.getSender());

                // 写入到数据通道,返回给客户端

                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                oos.writeObject(message2);

                ;



             }



             /**

              * 服务器关闭线程

              */

             if (msg.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)) {

                System.out.println(msg.getSender() + "退出");

                ManageServerConnClientThread.remove(userid);

                socket.close();

                break;

             }



             /**

              * 服务器转发私聊消息

              */

             if (msg.getMesType().equals(MessageType.MESSAGE_COMM_MES)) {

                // 根据msg获取到getterid,得到接受者对应的线程

                String getterid = msg.getGetter();

                OutputStream outputStream = ManageServerConnClientThread.getThread(getterid).getsocket()

                   .getOutputStream();

             ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

             objectOutputStream.writeObject(msg);



           }

          

          

           /**

            * 服务器转发群发消息

            *

            */

           if (msg.getMesType().equals(MessageType.MESSAGE_TOALL)) {

             //遍历管理线程的集合,把所有的线程的socket都得到

             HashMap<String, ServerConnectionClientThread> hm = ManageServerConnClientThread.getHm();

             Iterator<String> iterator = hm.keySet().iterator();

             while(iterator.hasNext()){

                String onlineuserid = iterator.next();

                if(!onlineuserid.equals(msg.getSender())){//排除群发消息自己给自己

                   //进行转发

                   OutputStream outputStream = ManageServerConnClientThread.getThread(onlineuserid).getsocket()

                        .getOutputStream();

                   ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

                   objectOutputStream.writeObject(msg);



                }      

             }

  

           }

          

           /**

            *

            * 文件消息

            *

            */

        if(msg.getMesType().equals(MessageType.MESSAGE_FILE)){

             //进行转发

             OutputStream outputStream = ManageServerConnClientThread.getThread(msg.getGetter()).getsocket()

                   .getOutputStream();

             ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

             objectOutputStream.writeObject(msg);

            

           }

          

          



        } catch (Exception e) {

           e.printStackTrace();

        }

     }

   }



}

3.7 ServerMain

package com.weiwei.server.main;



import com.weiwei.qqserver.service.QQServerService;



public class ServerMain {



   public static void main(String[] args) {

      new QQServerService();



   }

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java-请多指教

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值