热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

AndroidSocket通信实现简单聊天室

这篇文章主要为大家详细介绍了Android网络编程之Socket通信实现简单聊天室,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

socket通信是基于底层TCP/IP协议实现的。这种服务端不需要任何的配置文件和tomcat就可以完成服务端的发布,使用纯java代码实现通信。socket是对TCP/IP的封装调用,本身并不是一种协议,我们通过socket来调用协议来跟服务端进行通信和数据的传输。socket就像客户端与服务端之间的一条信息通道,每一个不同的客户端都会建立一个独立的socket,双方都没有关闭连接的话,连接—也就是建立好的这条socket通道将一直保持,服务端要跟那一个客户端通信只需要找到对应的socket对象就可以进行数据传递。

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

一. 服务端:在客户端跟服务端通信之前,服务端必须先开启。首先来看一下服务端Socket的编写吧。服务端就是一个简单的java项目,由于聊天室可能会有多个客户端同时连接并发送消息,我们这里使用线程池来处理客户端的请求。

List list = new ArrayList();
ExecutorService executorService;
 BufferedReader br;
 private static final int PORT = 12345;
 private static final int POOL_SIZE = 5 ;


public Socket_Server() throws IOException {
 executorService = Executors.newFixedThreadPool(POOL_SIZE);
 ServerSocket serverSocket = new ServerSocket(PORT);
 System.out.println(serverSocket.getInetAddress().getHostAddress() + ":服务端就绪。");
 Socket client = null;
 while (true) {//为每一个连接到服务器的客户端分配一个线程进行消息的接收和发送
  client = serverSocket.accept();
  list.add(client);
  executorService.execute(new Service(client));
 }
 }

首先我们创建了一个大小为5的固定大小线程池,并创建端口号为12345的服务端socket接收客户端请求,通过一个while循环不断轮询来自服务端的连接请求,在while循环里面调用了serverSocket.accept();是线程进入阻塞状态,也就是说在没有接收到客户端的请求时,程序将一直停留在这里,当有客户端连接服务端是,代码开始往下走,我们把接收到的客户端socket放入list里面,这样我们就把所有连接到服务端的socket保存下来了,这样就使得我们可以随时对任一客户端进行数据传递。之后就是线程池调用execute执行一个线程,把连接过来的socket作为参数传进去。接下来分析下service的内容:

class Service implements Runnable {

 Socket client;
 BufferedReader br;
 String msg = "";

 public Service(Socket client) {
  this.client = client;
  try {
  br = new BufferedReader(new InputStreamReader(
   client.getInputStream()));
  msg = "用户:" + client.getInetAddress() + "加入了聊天室,当前人数:"
   + list.size();
  sendMsg();
  } catch (Exception e) {
  e.printStackTrace();
  }

 }

 public void run() {
  try {
  while (true) {

   if ((msg = br.readLine()) != null) {
   if(msg.equals("bye")){
    list.remove(this.client) ;
    br.close() ;
    msg = "用户:" + client.getInetAddress() + "离开了聊天室,当前人数:" + list.size();
    sendMsg() ;
    client.close() ;
    break ;
   }else{
    msg = client.getInetAddress() + "说:" + msg;
    sendMsg() ;
   }
   }
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
 }

 public void sendMsg() {//为每一个用户发送这个消息:msg
  PrintWriter pw;
  System.out.println(msg);
  for (Socket client : list) {
  try {
   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    client.getOutputStream())));
   pw.println(msg);
   pw.flush() ;
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }
 }

在service的构造方法中使用了作为参数传进来的socket,在里面我们通过这个socket获取输入流包装成一个BufferedReader,br = new BufferedReader(new InputStreamReader(client.getInputStream()));这里我们是主要是针对聊天,所以使用的是字符流进行数据的传输,这个类里面声明了一个成员变量msg,通过这个变量来给每个客户端发送信息。下面看下sendMsg方法:

public void sendMsg() {//为每一个用户发送这个消息:msg
  PrintWriter pw;
  System.out.println(msg);
  for (Socket client : list) {
  try {
   pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    client.getOutputStream())));
   pw.println(msg);
   pw.flush() ;
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }

这里我们通过遍历list里面的每个socket获得它的的输出流并且包装成PrintWriter 向客户端发送信息, pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())));向每一个客户端发送成员变量msg内容,在PrintWriter调用print之后一定要调用flush刷新输出流进行数据的传递,否则客户端无法接收到服务端发送的数据。接下来看这个类的住方法 run:

public void run() {
 try {
 while (true) {

  if ((msg = br.readLine()) != null) {
  if(msg.equals("bye")){
   list.remove(this.client) ;
   br.close() ;
   msg = "用户:" + client.getInetAddress() + "离开了聊天室,当前人数:" + list.size();
   sendMsg() ;
   client.close() ;
    break ;
   }else{
   msg = client.getInetAddress() + "说:" + msg;
  sendMsg() ;
  }
   }
  }
  } catch (Exception e) {
  e.printStackTrace();
  }
 }

与前面类似,也是通过一个while进行无限循环进行读取socket的输入流,如果内容不为空就调用sendmsg对每一个客户端进行信息发送,有个小小的处理就是如果发送过来的信息是bye的时候就断开对应socket的链接,退出聊天室。以上是对服务端的分析,接下来我们来看Android客户端。

二. 客户端:客户端基本与服务端一样,我们直接上代码吧。

 //首先还是贴出成员变量

 private Button send;
 private EditText edt_input;
 private TextView txt_content;
 private static final String SERVER_PATH = "172.16.10.18";
 private static final int PORT = 12345;
 private Socket client;
 private BufferedReader br;
 private PrintWriter pw;
 private StringBuffer cOntent= new StringBuffer();

 private void initView() {
 send = (Button) findViewById(R.id.send);
 edt_input = (EditText) findViewById(R.id.input);
 txt_cOntent= (TextView) findViewById(R.id.chat_content);
 // --------发起网络连接-----
 new Thread() {
  public void run() {
  try {
   client = new Socket(SERVER_PATH, PORT);
   br = new BufferedReader(new InputStreamReader(
    client.getInputStream()));
   pw = new PrintWriter(new BufferedWriter(
    new OutputStreamWriter(client.getOutputStream())));
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }.start();

 send.setOnClickListener(new OnClickListener() {

  @Override
  public void onClick(View v) {
  if (client != null && client.isConnected() && !client.isOutputShutdown()) {
   String input = edt_input.getText().toString();
   pw.println(input);
   pw.flush();
   ((EditText)findViewById(R.id.input)).setText("");
  }
  }
 });

 new Thread(this).start();
 }

首先还是传统的new一个thread来建立与服务端的连接,因为主线程不能访问网络,由于我们客户端肯定是只有当前这一个socket的,所以只有一个线程,不用跟服务端一样使用线程池了。连接一旦建立,获取socket的输入输出流来包装成对应的BufferedReader和PrintWriter:br = new BufferedReader(new InputStreamReader(client.getInputStream()));pw = new PrintWriter(new BufferedWriter( new OutputStreamWriter(client.getOutputStream())));由于这里只会使用一个socket,所以这里的相关变量都可以使用成员变量。然后走下来就是对send按钮的监听,点击发送的话,把内容发送给服务端,服务端接收到之后发送给每一个保持着链接的客户端。这个activity也是实现了runnable接口的,接下来看run方法:

public void run() {
 while (true) {
  if (client != null && client.isConnected()
   && !client.isInputShutdown()) {
  try {
   String response;
   if ((respOnse= br.readLine()) != null) {
   content.append(response + "\n");
   mHandler.sendEmptyMessage(UPDATE_CONTENT);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  }
 }
 }

这里跟服务端一样,通过一个while无限循环读取来自服务端的信息,一旦读取到信息之后就通过handler从子线程发送消息到主线程,主线程进行数据的更新,其实就是向显示聊天室内容的textview追加聊天内容并且setText上去:

Handler mHandler = new Handler() {
 public void handleMessage(Message msg) {
  switch (msg.what) {
  case UPDATE_CONTENT:
  txt_content.append(content);
  break;

  default:
  break;
  }
 };
 };

总体来说客户端还是比服务端容易点,没有涉及到并发,只需要做当前这个客户端对应的socket通信就行了。以上就是对socket的一个简单总结和在安卓里面的简单应用实现聊天室功能。效果图:

 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 如何将本地Docker镜像推送到阿里云容器镜像服务
    本文详细介绍将本地Docker镜像上传至阿里云容器镜像服务的步骤,包括登录、查看镜像列表、推送镜像以及确认上传结果。通过本文,您将掌握如何高效地管理Docker镜像并将其存储在阿里云的镜像仓库中。 ... [详细]
  • 探索新一代API文档工具,告别Swagger的繁琐
    对于后端开发者而言,编写和维护API文档既繁琐又不可或缺。本文将介绍一款全新的API文档工具,帮助团队更高效地协作,简化API文档生成流程。 ... [详细]
  • 本文回顾了2017年的转型和2018年的收获,分享了几家知名互联网公司提供的工作机会及面试体验。 ... [详细]
  • 使用JS、HTML5和C3创建自定义弹出窗口
    本文介绍如何结合JavaScript、HTML5和C3.js来实现一个功能丰富的自定义弹出窗口。通过具体的代码示例,详细讲解了实现过程中的关键步骤和技术要点。 ... [详细]
  • docker镜像重启_docker怎么启动镜像dock ... [详细]
  • Java 中重写与重载的区别
    本文详细解析了 Java 编程语言中重写(Override)和重载(Overload)的概念及其主要区别,帮助开发者更好地理解和应用这两种多态性机制。 ... [详细]
  • 本文将详细介绍通过CAS(Central Authentication Service)实现单点登录的原理和步骤。CAS由耶鲁大学开发,旨在为多应用系统提供统一的身份认证服务。文中不仅涵盖了CAS的基本架构,还提供了具体的配置实例,帮助读者更好地理解和应用这一技术。 ... [详细]
  • MySQL Debug 模式的实现与应用
    本文详细介绍了如何启用和使用 MySQL 的调试模式,包括编译选项、环境变量配置以及调试信息的解析。通过实际案例展示了如何利用调试模式解决客户端无法连接服务器的问题。 ... [详细]
  • Eclipse 中 JSP 开发环境配置指南
    本文详细介绍了如何在 Eclipse 集成开发环境中配置 JSP 运行环境,包括必要的软件下载、Tomcat 服务器的配置以及常见问题的解决方法。 ... [详细]
  • NFS(Network File System)即网络文件系统,是一种分布式文件系统协议,主要用于Unix和类Unix系统之间的文件共享。本文详细介绍NFS的配置文件/etc/exports和相关服务配置,帮助读者理解如何在Linux环境中配置NFS客户端。 ... [详细]
  • 探讨HTML中的DIV样式难题
    本文深入分析了HTML中常见的DIV样式问题,并提供了有效的解决策略。适合所有对Web前端开发感兴趣的读者。 ... [详细]
  • Netflix利用Druid实现高效实时数据分析
    本文探讨了全球领先的在线娱乐公司Netflix如何通过采用Apache Druid,实现了高效的数据采集、处理和实时分析,从而显著提升了用户体验和业务决策的准确性。文章详细介绍了Netflix在系统架构、数据摄取、管理和查询方面的实践,并展示了Druid在大规模数据处理中的卓越性能。 ... [详细]
  • 深入理解Lucene搜索机制
    本文旨在帮助读者全面掌握Lucene搜索的编写步骤、核心API及其应用。通过详细解析Lucene的基本查询和查询解析器的使用方法,结合架构图和代码示例,带领读者深入了解Lucene搜索的工作流程。 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • DeepMind迁入谷歌伦敦新总部:人工智能研究的新里程碑
    谷歌旗下的人工智能研究机构DeepMind已正式入驻位于伦敦国王十字车站潘克拉斯广场6号的新总部。这座现代化的办公大楼不仅为DeepMind提供了宽敞的研究空间,也象征着谷歌对AI技术发展的高度重视。 ... [详细]
author-avatar
闻汝婕环境_259
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有