`
海王子1994
  • 浏览: 43901 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

实现多人聊天——简单群聊服务器的实现

 
阅读更多

 作为一个现代人,我们对当前众多的聊天通信平台并不陌生,facebook,qq,微信等都是大部分人每天都会接触的。那你有想过构建一个自己打造的聊天室,按照自己喜欢的模式,然后和朋友一起使用吗?下面就讲下聊天室的前身——群聊服务器的实现,之后你就可以在其基础上设计独属的聊天室了。
     要实现群聊服务器,首先要先了解下面的几个问题?
   1.Socket 与ServerSocket区别!?
   2.程序中的阻塞机制是怎样的?怎样解决“阻塞”现象?
   3.群聊服务器至少需要几个类才能实现?各个类应该实现的功能?


接下来将结合代码,逐步带大家了解这些问题的答案,认识体会实现过程的思路与期间遇到的种种问题。


最先,当然就是服务器的创建。建立绑定在指定端口的服务器,然后调用服务器的接受方法,进入阻塞状态。这相当于你开了家咖啡厅,然后等待客人的光顾。不过比较遗憾的是,当前的咖啡厅只能迎接一个客人!!   

   ServerSocket server=new ServerSocket(port);//建立绑定在指定端口的服务器"+port);   
   while(true)
   {
   Socket client=server.accept();//调用服务器接受方法,进入阻塞状态
   System.out.println("连接上"+client.getRemoteSocketAddress());

   ..........}

 

 看着这几行代码,就涉及到我们上面提到的前两个问题:

 

对于第一个问题,ServerSocket是对应服务器,而Socket是对应客户端;ServerSocket用于绑定端点,提供服务,并接受连接请求。Socket就是普通的连接套接字,在建立网络连接时使用的;在连接成功时,应用程序两端都会产生一个Socket实例,就相当于两头连通了一根电话线,能完成所需的会话。因此,真正进行通信的是服务器端的Socket与客户端的Socket,在ServerSocket 调用accept方法之后,它就“默默退出舞台“,由accept方法产生的Socket主权。

 

那么程序中的阻塞机制又是怎么回事呢?事实上,当ServerSocket调用accept方法时,会产生服务端的Socket实例,在没有用户连接上绑定的端口号时,此Socket会一直等待连接,相当于”阻塞“。当一个用户连接端口号后,服务端的Socket和用户的Socket就会连接上。若有其他用户连接端口号,会出现无法连接服务器的现象,直到之前连接的用户与服务器断开连接后才能轮到下一位,简而言之就是”一对一“;这也即”阻塞“现象。那怎么破呢???答案就是我们熟悉的线程!一个线程处理一个用户的Socket,这样多线程处理多用户,就能完美解决”僧多粥少“的困境。

 

因此我们要创建一个线程类,负责处理与用户的连接。当新的用户连接上端口号,就会创建一个线程,传入一个连接。

 

/*
 * 线程类,实现一个线程绑定一个处理对象
 */
public class ServerThread extends Thread{

 private  Socket client;
 private OutputStream ous;
 private UserInfo user;
 
 //构造函数
 public ServerThread(Socket cs)
 {
  this.client=cs;
 }
 
 //取得线程对象所处理的用户对象
 public UserInfo getRelatedUser()
 {
  return this.user;
 }
  
  //处理连接对象的方法,传送服务器与客户端之间的字符串实现聊天功能
  private void Process(Socket client) throws IOException
  {
     
   InputStream ins=client.getInputStream();
      ous=client.getOutputStream();
   //将输入流封ins装为可以读取一行字符串,也就是以\r\n结尾的字符串
      BufferedReader bfr=new BufferedReader(new InputStreamReader(ins));
    
      //获取到用户输入的用户名与密码
      readmsg("请输入你的用户名");
      String username=bfr.readLine();
      readmsg("请输入你的密码");
      String pwd=bfr.readLine();
     
      //创建UserInfo对象,将它用所给信息实体化,以待与数据库中信息比较核实
      user=new UserInfo();
      user.setUsername(username);
      user.setCode(pwd);
     
      //调用数据库模块,验证用户信息是否存在
      boolean loginstate=DaoTool.UserLogin(user);
      if(!loginstate)
      {
       readmsg("您输入的用户不存在,请重新输入");
       this.closeUp();
       return;
      }    
     
      ChatTool.addClient(this);//调用管理处理类中的方法
     
   String s="你好,可以开始与服务器正式通话";
   this.readmsg(s);
   
   //String input=ReadString(ins);//调用读取字符串的方法,读取输入流中的字符串
   String input=bfr.readLine();//一行行读取用户输入的信息
   while(!input.equals("bye"))
   {
    System.out.println(this.user.getUsername()+"说:"+input);
    //将每条信息传到其他客户上
    ChatTool.sendMsg(this.user, input);
    //s="服务器收到:"+input+"\r\n";
    //this.readmsg(s);
    //input=ReadString(ins);//接受下一次通话信息
    input=bfr.readLine();
   }
   
   ChatTool.clearClient(this);//用户下线,调用管理类的方法
   
   s="通话结束,欢迎再次连接";
   this.readmsg(s);
   
   this.closeUp();
  }
  //读取信息的方法,传入前信息不用加\r\n
  public void readmsg(String str)
  {
   str+="\r\n";
   byte[] data=str.getBytes();
   try {
    ous.write(data);
    ous.flush();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
  }
  
  //读取字符串的方法
  private String ReadString(InputStream ins) throws IOException
  {
   StringBuffer stb=new StringBuffer();//创建字符串缓冲区
   char c=0;
   //读取字符串,当按下空格键时表示一个字符串输入完成
   while(c!=13)
   {
    
    int ch=ins.read();
    c=(char)ch;//强制转换,将c转换成字符型
    stb.append(c);//将读取的字符添加到字符串缓冲区上   
   }
   String str=stb.toString().trim();//读取字符串缓冲区中的完整字符串,去掉空格
   return str;
  }
  public void run()
  {
   //在线程中调用连接处理方法     
    try {
     Process(this.client);
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }   
   //处理方法结束后,线程就退出
   
  }
  
  //关闭线程处理对象
  public void closeUp()
  {
   try {
    client.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
}

其中有几个地方要解释下,归纳如下:

1、输出要调用flush函数:flush函数是把缓冲区的数据强行输出。在读写时,数据是先被读到内存当中,后再写到文件里;故当数据读完并不意味着数据写完,若此时调用close方法关闭IO流,遗留在内存缓冲区上的数据就会流失。所以要在close前先flush一下,正如买的饮料要喝完才丢掉瓶子!

2.发送字符串时,首先调用字符串的 getBytes()方法,得到组成这个字符串的字节数组,发送出的实际上是这个字节数组。读取字符串的时候,有两种方法。第一种,因为系统本身是一个字节一个字节的读取,我们

可以创建一个字符串缓冲区,每次把读的字节转换成字符形式再放进去,当读到回车再把缓冲区的数据输出来,如此便能读取字符串了。第二种,就是使用了系统提供的 BufferedReader API,包装了从 Socket
上得到的输入流对象,调用其已有的 readLine()方法读取一行字符串,如此在读取中文时不会乱码。

 

对于群聊服务器,其实最初应该有一个验证环节。用户输入用户名和密码,然后系统核实后才能连接上。所以还需要有三个类:用户信息类、数据访问类、连接处理类。

 

1)用户信息类,就是定义用户属性,如用户名、密码、登陆时间、地址等等。

 

2)数据访问类,即是对用户数据进行增、删、查、改,即 CRUD 操作。这样的一个类命名时,通常以 Dao 作为前缀( Data Access Object)。这里暂时只提供确认用户存在的方法,其他方法大家可以自行添加。

 

/*
 * 数据对象访问类
 */
public class DaoTool {
    
 //用户信息数据库,储存所有用户信息
 private static Map<String,UserInfo> UserDP=new HashMap();
 
 //确认用户存在的方法,核对用户名是否与用户信息数据库中的匹配
 public static boolean UserLogin(UserInfo ui)
 {
  if(UserDP.containsKey(ui.getUsername()))
  {
   return true;
  }
  
  System.out.println("您输入的用户不存在,请重新输入!");
  return false;
 }
 
 //静态块,在类自动加载之前先存入10个用户信息
 static
 {
  for(int i=0;i<10;i++)
  {
   UserInfo user=new UserInfo();
   user.setUsername("user"+i);
   user.setCode("pwd"+i);
   UserDP.put(user.getUsername(), user);//存入用户信息数据库
  }
 }
 
}

这里出现了静态块,它里面的内容会在类加载之前就先执行。java中的类加载机制分为预先加载和依需求加载。java运行所需的基本类是预先加载,而我们平常用的new关键字就是进行依需求加载;如定义一个类实例时,Student stu=new Student();此时JRE才真正把Student类加载进来。

 

 

3)用户连接类,用于管理连接处理的线程对象。

 

//连接处理类,管理连接处理线程对象
public class ChatTool {
 
   private static List <ServerThread>stList=new ArrayList();//创建队列,存储所有的连接处理线程
   private ChatTool(){};//设置构造器私有,其他类无法生成该类对象,但可以调用其方法,适用于工具类

   /*
    * 当新的用户连接上服务器时,会同时产生一个连接处理类线程对象,把这个线程添加到队列中
    */
 public static void addClient(ServerThread st) throws IOException
 {
 stList.add(st);//添加线程到队列上
 sendMsg(st.getRelatedUser(),"我上线了,大家好!!目前在线人数:"+stList.size()); 
 
 }
/*
 * 当用户退出连接时,对应的线程对象也从队列中移除
 */
 public static void clearClient(ServerThread st)
 {
  stList.remove(st);//从队列中移除线程
  sendMsg(st.getRelatedUser(),st.getRelatedUser().getUsername()+"下线了,目前在线人数:"+stList.size());
 }
 
 /*
  * 发送信息给每个在线的用户
  */
public static void sendMsg(UserInfo user,String msg)
{

 msg=user.getUsername()+"说"+msg;
 
 for(int i=0;i<stList.size();i++)
 {
  ServerThread st=stList.get(i); //从队列中获取到所有用户
  st.readmsg(msg);//将信息输出给每个用户
 }
 
}

}

 

这里大家应该会注意到构造器的私有设定!当构造器私有时,它只能被包含它的类自身所访问,而无法在类的外部调用,故而可以阻止对象的生成。这种构造器私有的用法一般是针对工具类的,如字符串的验证、枚举转换之类的,可以认为是作为静态接口被外部调用。我们不需要实例它们,只是需要类名调用到它们里面的方法即可。

 

 

 

通过这5个类,我们就能实现简单的群聊服务器了!

 



 

 

 

 

 

 

 


                 

       

      

            

 










 

  • 大小: 84.2 KB
分享到:
评论
1 楼 梳子不爱头发 2015-03-28  
不错哦,加油加油

相关推荐

    使用WebSocket+SpringBoot搭建简易的多人聊天室

    使用WebSocket+SpringBoot搭建简易的多人聊天室 由浅入深,配合博客入门教程文章食用,风味独特。 使用WebSocket+SpringBoot搭建简易的多人聊天室 由浅入深,配合博客入门教程文章食用,风味独特。 使用WebSocket+...

    用JavaSocket编程开发多人聊天室(群聊、私聊等)

    2. 可以实现群聊(聊天记录显示在所有客户端界面)。 3. 完成好友列表在各个客户端上显示。 4. 可以实现私人聊天,用户可以选择某个其他用户,单独发送信息。 5. 服务器能够群发系统消息,能够强行让某些用户下线。 ...

    Linux下用C语言基于消息队列编写多人聊天室

    Linux 环境下利用消息队列消息机制,多线程通信,字符串处理,链表操作,信号简单处理等知识用C语言编写多人聊天室实现: 服务器实现各用户之间聊天的消息转发,在用户注册或者登录时对各用户进行消息提醒,客户端从...

    基于Linux的TCP多人聊天室

    C语言编写,采用CS模型,TCP、UDP网络协议实现的内网多人聊天室。 服务器:采用多线程以及线程锁处理客户端的所有请求以及信息转发任务。服务端实时显示客户的登录与登出信息;保存客户上传的共享文件(网盘功能);...

    Python实现的多人聊天室源码,基于socket tcp通信,使用tkinter做客户端界面,含可执行文件

    Python实现的多人聊天室源码,基于socket tcp通信,使用tkinter做客户端界面;一个多人同时在线的聊天系统;

    MFC多人聊天室_聊天室_MFC网络编程多人实例多人聊天室_

    MFC网络编程,多线程开发实例,使用多线程事务模式,搭建多人聊天室,实现消息的及时响应,多个用户可以随时加入聊天。

    Springboot+websocket 实现多人聊天室/单人聊天

    这是一个简单使用websocket实现多人聊天室,单人聊天室的demo,里面使用的是最原始的websocket的方法,附有客户端界面可以直接跑起来发送消息看效果。 该项目对应刚刚接触websocket技术是比较有用的。 2019-10-11的...

    MFC多人聊天室

    MFC的多人聊天室,可以用哦。聊天室服务器聊天室服务器客户端。

    jQuery实现多人在线群聊功能

    项目案例是基于Struts2实现的,ajax技术是使用jQuery中的,项目就只有一个页面,多人可以访问然后实现群聊。如果想看详细的信息可以去http://www.biezuomeng.com/codeDatil?id=34看看!

    webscoket实现多人聊天(内置聊天机器人)

    各位老铁们,我也想要一点积分,提问时需要积分,目前需要点积分,后面我会免费发出来,谢谢老铁,主要功能多人聊天,功能类似QQ群,内置了聊天机器人,没人聊天,机器人陪你聊天,没用到springboot,后续我会发...

    基于mfc的多人聊天室

    基于mfc的多人聊天室,采用c/s结构,可实现公共频道和私人频道的聊天方式

    多人聊天室python实现

    多人聊天室python实现 功能简介:  群聊功能: 一个聊天窗口发消息全部都能收到  私聊功能: 只能给特定的IP和端口或者用户名发消息  查看在线用户功能: 可以查看当前在线用户  上传下载功能: 用户可以从文件...

    网页版多人聊天室系统

    多人聊天室,功能基本已经实现,可供参考,兼容ie8,给别人做的毕业设计,实际企业应用时建议加密传输。 SockJS+Spring+SpringMVC+Mybaties+EasyUI+Mysql+Tomcat7+Jdk7 博客地址:...

    linux多人聊天室程序(udp协议)

    使用udp协议实现的服务器与客户端多人聊天室功能 详细代码 实现同一网段下不同ip地址间的通信

    C语言基于socket多人聊天(包含注册登录)

    C语言基于socket多人聊天(包含注册登录),有注册和登录模块,验证通过才可以发信息。可实现多人同时在线发信息,或者一对一发信息

    C# ,WinForm 多人聊天

    C# 4.0 利用Socket套接字写的Winform多人聊天功能,有服务端Server,和客户端Client,目前只支持局域网的多人聊天,可用于C#学习使用,和Socket的了解。

    jsp网络聊天系统——群聊系统

    jsp网络聊天系统——群聊系统,一个完整的工程,导入即可使用,一个不错的框架!

    java swing+socket实现多人聊天程序

    java 利用swing+socket实现多人聊天程序,分客户端,服务端,运行效果参考博客 https://blog.csdn.net/wcc27857285/article/details/84038401

    JAVA多人聊天经典源码

    JAVA多人聊天,同时在线,可群聊可单聊,经典程序,立马上手,编制自己风格的在线聊天系统

    Java代码写的局域网多线程TCP、Socket多人聊天室winsock.zip

    Java代码写的基于TCP和Socket编程的局域网多线程多人聊天室(Java网络编程)实训小例子。

Global site tag (gtag.js) - Google Analytics