Android使用多线程进行网络聊天室通信

TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信了。Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端通信接口,并通过Socket产生IO流来进行网络通信。

下面的程序Demo是实现一个简单的C/S聊天室的应用,每个客户端该包含两条线程:一条负责生成主界面,响应用户动作,并将用户输入的数据写入Socket对应的输出流中;另一条负责读取Socket对应的输入流中的数据(从服务器发送过来的数据),并负责将这些数据在程序界面上显示出来。
客户端程序是一个Android应用,因此需要创建一个Android项目,这个Android应用的界面中包含两个文本框:一个用于接收用户的输入;另一个用于显示聊天信息。界面中还有一个按钮,当用户单击该按钮时,程序向服务器发送聊天信息。
layout/activity_main.xml界面布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">

 <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal">
  <!-- 定义一个文本框,它用于接收用户的输入 -->
  <EditText
   android:id="@+id/input"
   android:layout_width="280dp"
   android:layout_height="wrap_content" />

  <Button
   android:id="@+id/send"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:paddingLeft="8dp"
   android:text="发送" />
 </LinearLayout>
 <!-- 定义一个文本框,它用于显示来自服务器的信息 -->
 <TextView
  android:id="@+id/show"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#ffff"
  android:gravity="top"
  android:textColor="#f000"
  android:textSize="18sp" />
</LinearLayout>

客户端的Activity负责生成程序界面,并为程序的按钮单击事件绑定事件监听器,当用户单击按钮时向服务器发送信息。
MainActivity.java逻辑代码如下:

package com.fukaimei.multithreadclient;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

 // 定义界面上的两个文本框
 EditText input;
 TextView show;
 // 定义界面上的一个按钮
 Button send;
 Handler handler;
 // 定义与服务器通信的子线程
 ClientThread clientThread;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  input = (EditText) findViewById(R.id.input);
  send = (Button) findViewById(R.id.send);
  show = (TextView) findViewById(R.id.show);
  handler = new Handler() // ②
  {
   @Override
   public void handleMessage(Message msg) {
    // 如果消息来自于子线程
    if (msg.what == 0x123) {
     // 将读取的内容追加显示在文本框中
     show.append("\n" + msg.obj.toString());
    }
   }
  };
  clientThread = new ClientThread(handler);
  // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
  new Thread(clientThread).start(); // ①
  send.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    try {
     // 当用户按下发送按钮后,将用户输入的数据封装成Message
     // 然后发送给子线程的Handler
     Message msg = new Message();
     msg.what = 0x234;
     msg.obj = input.getText().toString();
     clientThread.revHandler.sendMessage(msg);
     // 清空input文本框
     input.setText("");
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
  });
 }
}

当用户单击该程序界面中的“发送”按钮后,程序将会把input输入框中的内容发送给clientThread的revHandler对象,clientThread负责将用户输入的内容发送给服务器。

ClientThread子线程负责建立与远程服务器的连接,并负责与远程服务器通信,读到数据之后便通过Handler对象发送一条消息;当ClientThread子线程收到UI线程发送过来的消息后,还负责将用户输入的内容发送给远程服务器。

ClientThread.java逻辑代码如下:

package com.fukaimei.multithreadclient;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class ClientThread implements Runnable {

 private static final String TAG = "ClientThread";
 private Socket s;
 // 定义向UI线程发送消息的Handler对象
 private Handler handler;
 // 定义接收UI线程的消息的Handler对象
 public Handler revHandler;
 // 该线程所处理的Socket所对应的输入流
 BufferedReader br = null;
 OutputStream os = null;

 public ClientThread(Handler handler) {
  this.handler = handler;
 }

 public void run() {
  try {
   s = new Socket("172.xx.xx.xxx", 30000);
   br = new BufferedReader(new InputStreamReader(
     s.getInputStream()));
   os = s.getOutputStream();
   // 启动一条子线程来读取服务器响应的数据
   new Thread() {
    @Override
    public void run() {
     String content = null;
     // 不断读取Socket输入流中的内容
     try {
      while ((content = br.readLine()) != null) {
       // 每当读到来自服务器的数据之后,发送消息通知程序
       // 界面显示该数据
       Message msg = new Message();
       msg.what = 0x123;
       msg.obj = content;
       handler.sendMessage(msg);
      }
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }.start();
   // 为当前线程初始化Looper
   Looper.prepare();
   // 创建revHandler对象
   revHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
     // 接收到UI线程中用户输入的数据
     if (msg.what == 0x234) {
      // 将用户在文本框内输入的内容写入网络
      try {
       os.write((msg.obj.toString() + "\r\n")
         .getBytes("utf-8"));
      } catch (Exception e) {
       e.printStackTrace();
      }
     }
    }
   };
   // 启动Looper
   Looper.loop();
  } catch (SocketTimeoutException e1) {
   Log.d(TAG, "网络连接超时!");
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

上面线程的功能也非常简单,它只是不断地获取Socket输入流中的内容,当读到Socket输入流中的内容后,便通过Handler对象发送一条消息,消息负责携带读到的数据。除此之外,该子线程还负责读取UI线程发送的消息,接收到消息之后,该子线程负责中携带的数据发送给远程服务器。

服务器端应该包含多条线程,每个Socket对应一条线程,该线程负责读取Socket对应输入流,并将读到的数据向每个Socket输出流发送一遍,因此需要在服务器端使用List来保存所有的Socket。
下面是服务器端的代码。程序为服务器提供了两个类:一个是创建ServerSocket监听的主类;另一个是负责处理每个Socket通信的线程类。

/MultiThreadServer/src/MyServer.java逻辑代码如下:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class MyServer {

 // 定义保存所有Socket的ArrayList
 public static ArrayList<Socket> socketList = new ArrayList<Socket>();

 public static void main(String[] args) throws IOException {
  ServerSocket ss = new ServerSocket(30000);
  while (true) {
   // 此行代码会阻塞,将一直等待别人的连接
   Socket s = ss.accept();
   socketList.add(s);
   // 每当客户端连接后启动一条ServerThread线程为该客户端服务
   new Thread(new ServerThread(s)).start();
  }
 }
}

上面的程序是服务器端只负责接收客户端Socket的连接请求,每当客户端Socket连接到该ServerSocket之后,程序将对应Socket加入socketList集合中保存,并为该Socket启动一条线程,该程序负责处理该Socket所有的通信任务。服务器端线程类的代码如下。

/MultiThreadServer/src/ServerThread.java逻辑代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Iterator;

// 负责处理每条线程通信的线程类
public class ServerThread implements Runnable {
 // 定义当前线程所处理的Socket
 Socket s = null;
 // 该线程所处理的Socket所对应的输入流
 BufferedReader br = null;
 public ServerThread(Socket s) throws IOException {
  this.s = s;
  // 初始化该Socket对应的输入流
  br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8"));
 }
 @Override
 public void run() {
  String content = null;
  // 采用循环不断从Socket中读取客户端发送过来的数据
  while ((content = readFromClient()) != null) {
   // 遍历socketList中的每个Socket
   // 将读取的内容向每个Socket发送一次
   for (Iterator<Socket> it = MyServer.socketList.iterator(); it.hasNext();) {
    Socket s = it.next();
    try {
     OutputStream os = s.getOutputStream();
     os.write((content + "\n").getBytes("utf-8"));
    } catch (Exception e) {
     e.printStackTrace();
     // 删除该Socket
     it.remove();
     System.out.println(MyServer.socketList);
    }
   }
  }
 }
 // 定义读取客户端数据的方法
 private String readFromClient() {
  try {
   return br.readLine();
  } catch (IOException e) { // 如果捕获到异常,表明该Socket对应的客户端已经关闭
   e.printStackTrace();
   // 删除该Socket
   MyServer.socketList.remove(s);
  }
  return null;
 }
}

上面的服务器端线程类不断读取客户端数据,程序使用readFromClient()方法来读取客户端数据,如果在读数据过程中捕获到IOException异常,则表明该Socket对应的客户端Socket出现问题,程序就将该Socket从socketList中删除。
当服务器线程读到客户端数据之后,程序遍历socketList集合,并将该数据向socketList集合中的每个Socket发送一次——该服务器线程将把从Socket中读到的数据向socketList中的每个Socket转发一次。

先运行上面程序的MyServer类,该类运行后只是作为服务器,看不到任何输出。接着可以运行Android客户端——相当于启动聊天界面登录该服务器,接下来在任何一个Android客户端输入一些内容后单击“发送”按钮,将可以看到所有客户端(包含自己)都会收到刚刚输入的内容,这样就简单实现了一个C/S结构的聊天室的功能。
注意:由于该程序需要访问互联网,因此还需要在清单文件AndroidManifest.xml文件中授权访问互联网的权限:

<!-- 授权访问互联网-->
 <uses-permission android:name="android.permission.INTERNET" />

Demo程序运行效果界面截图如下:

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

(0)

相关推荐

  • android socket聊天室功能实现

    前提概要 笔者很久之前其实就已经学习过了socket,当然也是用socket做过了聊天室,但是觉得此知识点比较一般,并无特别难的技术点,于是也并未深究. 然而近期一个项目中对socket的使用却让笔者感觉socket强大无比,可以实现诸多功能. 个人Socket体验 项目主要有关智能家居,需要实现多台手机同时对灯进行操作(开或者关),主要需要实现以下几点: 1.进入界面时获取所有灯的状态. 2.一台手机改变了灯的状态,其他的手机上可以有所显示. 3.硬件上改变了灯的状态(手动开关灯),所有手机上

  • Android中基于XMPP协议实现IM聊天程序与多人聊天室

    简单的IM聊天程序 由于项目需要做一个基于XMPP协议的Android通讯软件.故开始研究XMPP. XMPP协议采用的是客户端-服务器架构,所有从一个客户端发到另一个客户端的消息和数据都必须经过XMPP服务器转发,而且支持服务器间DNS的路由,也就是说可以构建服务器集群,使不同的 服务器下的客户端也可以通信,XMPP的前身是一个开源组织制定的网络通信协议--Jabber,XMPP的核心是在网络上分片段发送XML流的协议,这个协议是XMPP的即时通讯指令的传递手段.       为了防止服务器间

  • Android 基于Socket的聊天室实例

    Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信. Client A  发信息给 Client B ,  A的信息首先发送信息到服务器Server ,Server接受到信息后再把A的信息广播发送给所有的Clients 首先我们要在服务器建立一个ServerSocket ,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待

  • Android编写简单的聊天室应用

    最近写了一个简单的聊天室应用,可以发送表情,更改头像这些功能.主要技术点就是怎样把表情图片放到textview等Ui控件中展示.这里废话不多说,下面是效果图: 这里主要讲下怎样把文本替换到表情,先说下思路,首先我们的图片是保存在本地资源目录drawable中而所有的资源文件都是R这个类来管理,所以我们可以利用正则表达式找出图片id包装成ImageSpan然后把ImageSpan放到SpannableString中,最后把SpannableString放入edittext中,下面是源码: pack

  • Android使用多线程进行网络聊天室通信

    TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信了.Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端通信接口,并通过Socket产生IO流来进行网络通信. 下面的程序Demo是实现一个简单的C/S聊天室的应用,每个客户端该包含两条线程:一条负责生成主界面,响应用户动作,并将用户输入的数据写入Socket对应的输出流中:另一条

  • C++基于socket多线程实现网络聊天室

    本文实例为大家分享了C++基于socket多线程实现网络聊天室的具体代码,供大家参考,具体内容如下 1. 实现图解 2. 聊天室服务端:TCP_Server_Chat.cpp #include <winsock2.h> // winsock2的头文件 #include <iostream> #pragma comment(lib, "ws2_32.lib") using namespace std; // stdcall的线程处理函数 DWORD WINAPI

  • Python实现网络聊天室的示例代码(支持多人聊天与私聊)

    实验名称: 网络聊天室 功能: i. 掌握利用Socket进行编程的技术 ii. 掌握多线程技术,保证双方可以同时发送 iii. 建立聊天工具 iv. 可以和单人聊天 v. 可以和多个人同时进行聊天 vi. 使用图形界面,显示双方的语录 vii. 程序可以在一定程度上进行错误识别 概述 实验通过聊天室可以完成单人或多人之间的聊天通信,功能的实现主要是通过Socket通信来实现.本次实验采用客户端/服务器(C/S)架构模式,通过Python语言来编写服务器端与客户端的程序.运用多线程可完成多点对多

  • 基于Python socket实现简易网络聊天室

    目录 1.socket_ui.py 服务端 1-1. 依赖引用 1-2. 实现过程 1-3. 实现效果 2.client_ui.py 客户端 2-1. 依赖引用 2-2. 实现过程 2-3. 实现效果 在这个周末刚刚写出来的python桌面应用--网络聊天室,主要通过pyqt5作为桌面应用框架,socket作为网络编程的框架,从而实现包括客户端和服务端的网络聊天室的GUI应用,希望可以一起学习.一起进步! 应用包括服务端server_ui.py.客户端client_ui.py两个python模块

  • 使用socket实现网络聊天室和私聊功能

    使用socket技术实现网络聊天室和私聊功能,具体内容如下 话不多说先上图: 1.聊天室群聊页面 在线用户的联系人列表 socket连接页面 私聊页面 项目介绍 与服务端实现socket连接:每个客户端连接到服务器的时候,服务器会将每个连接的socket保存在list集合中. 群聊功能:当有一个用户发送群聊消息给服务器的时候,服务器会将所有信息转发给list列表中的所有已连接的客户端. 私聊功能:用户发送私聊信息给服务器后,服务器会向一个目标ip发送消息. 显示在线联系人列表:当有新用户登录成功

  • python实现文本界面网络聊天室

    Hello大家好,今天说一下python的socket编程,基于python的socket通信的文本框网络聊天 首先,实验环境: 一个云服务器(我们这里是用的阿里云,大家将就自己的条件吧): 类Unix操作系统(如Mac OS,Linux等): Windows系列操作系统. 在这里,我使用的是阿里云,Mac OSX,Windows XP(在mac上的一个虚拟机). Server.py # -*- coding: utf-8 -*- #!/usr/local/bin/python import s

  • Qt实现网络聊天室的示例代码

    目录 1. 效果演示 2. 预备知识 2.1 QTcpServer 2.2 QTcpServer信号 2.3 QTcpSocket 2.4 QTcpSocket信号 3. 通信流程 3.1 服务器端 3.2 客户端 1. 效果演示 客户端 服务器 连接成功之后 2. 预备知识 在Qt中,实现网络编程的方式比用C++或C实现要方便简单许多,因为Qt已经替我们封装好了,我们会使用就可以了,然后大家还需要了解Qt 的信号槽机制,可以参考我这篇文章,Qt信号槽 2.1 QTcpServer QTcpSe

  • C语言实现简易网络聊天室

    本文实例为大家分享了C语言实现网络聊天室的具体代码,供大家参考,具体内容如下 业务逻辑: 1.客户端注册名字 2.告诉所有在线的客户端,XXX进入聊天室 3.新建一个线程为该客户端服务,随时接收客户端发送来的消息 4.当接收到一个客户端的消息时,向每一个客户端转发一份(群聊) 5.同时在线人数最多50人 任何客户端可以随意随时进入或退出客户端 服务端代码server.c #include <stdio.h> #include <stdlib.h> #include <stri

  • QT基于TCP网络聊天室

    本文实例为大家分享了QT实现网络聊天室的具体代码,供大家参考,具体内容如下 1.客户端 1.1UI设计 分两个部分,第一部分是消息区里面包含QPlainTextEdit和QListWidget,要显示接收的消息和在线的成员.第二部分QLineEdit发生字符. 1.2 子模块 1.2.1 登录界面 登录界面主要就是要有验证码,防止恶意程序的攻击.通过paintEvent画出一个白色矩形,在白色矩形里面显示四个不同颜色的字母以及随机出现的噪点. 代码: QLoginDialog.h #ifndef

  • QT实现TCP网络聊天室

    本文实例为大家分享了QT实现TCP网络聊天室的具体代码,供大家参考,具体内容如下 服务器: serverdialog.h #ifndef SERVERDIALOG_H #define SERVERDIALOG_H #include <QDialog> #include <QTcpServer> #include <QTcpSocket> #include <QDebug> #include <QTimer> namespace Ui { clas

随机推荐