应用效果图:

                                              

客户端                                                                                                          服务器端

 先打开手机服务器,使客户端在同一ip下即可完成wifi热点下通信

一、服务器端

服务器端是用Socket 实现,Socket基础可参考我的上一篇博文《手机服务器微架构设计与实现 之 http server》代码如下:

所需权限:

  1. <uses-permission android:name="android.permission.INTERNET"/>
  2. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  3. <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
  1. package com.example.lifen.serverdemo;
  2.  
  3. import android.os.Bundle;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.util.Log;
  6. import android.widget.TextView;
  7.  
  8. import java.net.InetAddress;
  9. import java.net.NetworkInterface;
  10. import java.net.SocketException;
  11. import java.util.ArrayList;
  12. import java.util.Collections;
  13.  
  14. public class MainActivity extends AppCompatActivity {
  15.  
  16. private MyServer ms;
  17. public static String ipAddress;
  18. private TextView ip;
  19.  
  20. @Override
  21. protected void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_main);
  24.  
  25. ms = new MyServer();
  26. ms.startAsync();
  27. ip = (TextView) findViewById(R.id.ip);
  28. ipAddress = getLocalIpAddress();
  29. ip.setText(ipAddress);
  30. }
  31.  
  32. @Override
  33. protected void onDestroy() {
  34. super.onDestroy();
  35. ms.stopAsync();
  36. }
  37.  
  38. public String getLocalIpAddress() {
  39. try {
  40. String ipv4;
  41. ArrayList<NetworkInterface> nilist = Collections.list(NetworkInterface.getNetworkInterfaces());
  42. for (NetworkInterface ni: nilist)
  43. {
  44. ArrayList<InetAddress> ialist = Collections.list(ni.getInetAddresses());
  45. for (InetAddress address: ialist){
  46. if (!address.isLoopbackAddress() && !address.isLinkLocalAddress())
  47. {
  48. ipv4=address.getHostAddress();
  49. return ipv4;
  50. }
  51. }
  52.  
  53. }
  54.  
  55. } catch (SocketException ex) {
  56. Log.e("localip", ex.toString());
  57. }
  58. return null;
  59. }
  60. }
  1. package com.example.lifen.serverdemo;
  2.  
  3. /**
  4. * Created by LiFen on 2018/1/1.
  5. */
  6.  
  7. import java.io.BufferedReader;
  8. import java.io.IOException;
  9. import java.io.InputStreamReader;
  10. import java.io.OutputStream;
  11. import java.net.Socket;
  12. import java.net.SocketException;
  13. import java.util.Iterator;
  14.  
  15. public class ServerThread implements Runnable {
  16. Socket s = null;
  17. BufferedReader br = null;
  18.  
  19. public ServerThread(Socket s) throws IOException {
  20. this.s = s;
  21. br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8"));
  22. }
  23.  
  24. public void run() {
  25. try {
  26. String content = null;
  27. while ((content = readFromClient()) != null) {
  28. for (Iterator<Socket> it = MyServer.socketList.iterator(); it.hasNext();) {
  29. Socket s = it.next();
  30. try {
  31. OutputStream os = s.getOutputStream();
  32. os.write((content + "\n").getBytes("utf-8"));
  33. } catch (SocketException e) {
  34. e.printStackTrace();
  35. it.remove();
  36. System.out.println(MyServer.socketList);
  37. }
  38. }
  39. }
  40. } catch (IOException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44.  
  45. private String readFromClient() {
  46. try {
  47. return br.readLine();
  48. } catch (IOException e) {
  49. e.printStackTrace();
  50. MyServer.socketList.remove(s);
  51. }
  52. return null;
  53. }
  54. }
  1. package com.example.lifen.serverdemo;
  2.  
  3. import java.io.IOException;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. import java.util.ArrayList;
  7.  
  8. /**
  9. * Created by LiFen on 2018/1/1.
  10. */
  11.  
  12. public class MyServer{
  13. public static ArrayList<Socket> socketList = new ArrayList<>();
  14. private boolean isEnable;
  15. private ServerSocket socket;
  16.  
  17. /**
  18. * 启动Server(异步)
  19. */
  20. public void startAsync(){
  21. isEnable = true;
  22. new Thread(new Runnable() {
  23. @Override
  24. public void run() {
  25. ServerSocket ss = null;
  26. try {
  27. ss = new ServerSocket(30000);
  28. while (true) {
  29. Socket s = ss.accept();
  30. socketList.add(s);
  31. new Thread(new ServerThread(s)).start();
  32. }
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }).start();
  38. }
  39.  
  40. /**
  41. * 停止Server(异步)
  42. */
  43. public void stopAsync() {
  44. if(!isEnable){
  45. return;
  46. }
  47. isEnable = false;
  48. try {
  49. socket.close();
  50. } catch (IOException e) {
  51. e.printStackTrace();
  52. }
  53. socket = null;
  54. }
  55. }

上面服务器线程类不断读取客户端数据,程序使用readFromClient()方法来读取客户数据,如果在读取数据过程中捕获到 Ioexception异常,则表明该 Socket对应的客户端 Socket出现了问题(到底什么问题我们不管,反正不正常),程序就将该 Socket从 socketlist中删除。

当服务器线程读到客户端数据之后,程序遍历 socketlist集合,并将该数据向 socketlist集合中的每个 Socket发送一次该服务器线程将把从 Socket中读到的数据向 socketlist中的每个 Socket转发一次。

二、客户端

  1. package com.example.lifen.multithreadclient;
  2.  
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.support.v7.app.AppCompatActivity;
  7. import android.util.Log;
  8. import android.view.View;
  9. import android.widget.Button;
  10. import android.widget.EditText;
  11. import android.widget.TextView;
  12.  
  13. import java.net.InetAddress;
  14. import java.net.NetworkInterface;
  15. import java.net.SocketException;
  16. import java.util.ArrayList;
  17. import java.util.Collections;
  18.  
  19. public class MainActivity extends AppCompatActivity {
  20. private static final String TAG = "MainActivity";
  21. private EditText input;
  22. private Button send;
  23. private TextView show;
  24. Handler handler;
  25. private ClientThread clientThread;
  26. private TextView ip;
  27. public static String ipAddress;
  28.  
  29. @Override
  30. protected void onCreate(Bundle savedInstanceState) {
  31. super.onCreate(savedInstanceState);
  32. setContentView(R.layout.activity_main);
  33.  
  34. input = (EditText) findViewById(R.id.input);
  35. send = (Button) findViewById(R.id.send);
  36. show = (TextView) findViewById(R.id.show);
  37. ip = (TextView) findViewById(R.id.ip);
  38. handler = new Handler(){
  39. @Override
  40. public void handleMessage(Message msg) {
  41. if(msg.what == 0x123){
  42. show.append("\n" + msg.obj.toString());
  43. }
  44. }
  45. };
  46. clientThread = new ClientThread(handler);
  47. new Thread(clientThread).start();
  48.  
  49. ipAddress = getLocalIpAddress();
  50.  
  51. ip.setText("当前IP: "+ipAddress);
  52.  
  53. send.setOnClickListener(new View.OnClickListener() {
  54. @Override
  55. public void onClick(View v) {
  56. try{
  57. Message msg = new Message();
  58. msg.what = 0x345;
  59. msg.obj = input.getText().toString();
  60. // Log.i(TAG, "onClick: "+msg.obj);
  61. clientThread.revHandler.sendMessage(msg);
  62. input.setText("");
  63. }catch (Exception e) {
  64. Log.e(TAG, "onClick: ", e);
  65. }
  66. }
  67. });
  68. }
  69.  
  70. public String getLocalIpAddress() {
  71. try {
  72. String ipv4;
  73. ArrayList<NetworkInterface> nilist = Collections.list(NetworkInterface.getNetworkInterfaces());
  74. for (NetworkInterface ni: nilist)
  75. {
  76. ArrayList<InetAddress> ialist = Collections.list(ni.getInetAddresses());
  77. for (InetAddress address: ialist){
  78. if (!address.isLoopbackAddress() && !address.isLinkLocalAddress())
  79. {
  80. ipv4=address.getHostAddress();
  81. return ipv4;
  82. }
  83. }
  84.  
  85. }
  86.  
  87. } catch (SocketException ex) {
  88. Log.e("localip", ex.toString());
  89. }
  90. return null;
  91. }
  92.  
  93. }
  1. package com.example.lifen.multithreadclient;
  2.  
  3. import android.os.Handler;
  4. import android.os.Looper;
  5. import android.os.Message;
  6. import android.util.Log;
  7.  
  8. import java.io.BufferedReader;
  9. import java.io.IOException;
  10. import java.io.InputStreamReader;
  11. import java.io.OutputStream;
  12. import java.net.Socket;
  13. import java.net.SocketTimeoutException;
  14.  
  15. /**
  16. * Created by LiFen on 2017/12/31.
  17. */
  18.  
  19. public class ClientThread implements Runnable {
  20. private static final String TAG = "ClientThread";
  21. private Socket s;
  22. //定义向UI线程发送消息的Handler 对象
  23. private Handler handler;
  24. //定义接收UI线程的消息 Handler对象
  25. public Handler revHandler;
  26. private BufferedReader br = null;
  27. private OutputStream os = null;
  28.  
  29. public ClientThread(Handler handler){
  30. Log.d(TAG, "ClientThread() called with: handler = [" + handler + "]");
  31. this.handler = handler;
  32. }
  33.  
  34. @Override
  35. public void run() {
  36. Log.d(TAG, "run() called");
  37. try{
  38. String iptemp = MainActivity.ipAddress;
  39. s = new Socket(iptemp,30000);
  40. br = new BufferedReader(new InputStreamReader(s.getInputStream()));
  41. os = s.getOutputStream();
  42. //启动一条子线程来读取服务器响应的数据
  43. new Thread(){
  44. @Override
  45. public void run() {
  46. String content = null;
  47. try {
  48. while((content = br.readLine()) != null){
  49. Message msg = new Message();
  50. msg.what = 0x123;
  51. msg.obj = content;
  52. handler.sendMessage(msg);
  53. }
  54. } catch (IOException e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. }.start();
  59. //为当前线程 初始化Looper
  60. Looper.prepare();
  61. //创建revHandler对象
  62. revHandler = new Handler(){
  63. @Override
  64. public void handleMessage(Message msg) {
  65. if(msg.what == 0x345){
  66. //将用户在文本框内输入网路
  67. try{
  68. os.write((msg.obj.toString() + "\r\n").getBytes("utf-8"));
  69. } catch (IOException e) {
  70. e.printStackTrace();
  71. }
  72. }
  73. }
  74. };
  75. // 启动Looper
  76. Looper.loop();
  77. }catch (SocketTimeoutException e1){
  78. Log.i(TAG, "run: 网络连接超时");
  79. } catch (Exception e) {
  80. e.printStackTrace();
  81. }
  82. }
  83. }

布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context="com.example.lifen.multithreadclient.MainActivity">
  8.  
  9. <EditText
  10. android:id="@+id/input"
  11. android:layout_width="244dp"
  12. android:layout_height="47dp"
  13. android:ems="10"
  14. android:inputType="textPersonName"
  15. tools:layout_constraintBottom_creator="1"
  16. android:layout_marginStart="45dp"
  17. app:layout_constraintBottom_toBottomOf="parent"
  18. tools:layout_constraintLeft_creator="1"
  19. android:layout_marginBottom="30dp"
  20. app:layout_constraintLeft_toLeftOf="parent"
  21. android:layout_marginLeft="45dp"
  22. app:layout_constraintRight_toLeftOf="@+id/send"
  23. android:layout_marginRight="8dp"
  24. app:layout_constraintHorizontal_bias="1.0"
  25. android:layout_marginEnd="8dp" />
  26.  
  27. <Button
  28. android:id="@+id/send"
  29. android:layout_width="wrap_content"
  30. android:layout_height="wrap_content"
  31. android:text="发送"
  32. tools:layout_constraintRight_creator="1"
  33. tools:layout_constraintBottom_creator="1"
  34. app:layout_constraintBottom_toBottomOf="@+id/input"
  35. android:layout_marginEnd="16dp"
  36. app:layout_constraintRight_toRightOf="parent"
  37. android:layout_marginRight="16dp" />
  38.  
  39. <TextView
  40. android:id="@+id/ip"
  41. android:layout_width="0dp"
  42. android:layout_height="21dp"
  43. android:text="当前IP:"
  44. android:ellipsize="marquee"
  45. android:focusable="true"
  46. android:focusableInTouchMode="true"
  47. android:maxLines="1"
  48. tools:layout_constraintRight_creator="1"
  49. tools:layout_constraintBottom_creator="1"
  50. app:layout_constraintBottom_toTopOf="@+id/send"
  51. android:layout_marginStart="22dp"
  52. android:layout_marginEnd="22dp"
  53. app:layout_constraintRight_toRightOf="parent"
  54. tools:layout_constraintLeft_creator="1"
  55. android:layout_marginBottom="5dp"
  56. app:layout_constraintLeft_toLeftOf="parent"
  57. app:layout_constraintHorizontal_bias="0.09" />
  58.  
  59. <ScrollView
  60. android:layout_width="0dp"
  61. android:layout_height="0dp"
  62. tools:layout_constraintTop_creator="1"
  63. tools:layout_constraintRight_creator="1"
  64. tools:layout_constraintBottom_creator="1"
  65. android:layout_marginStart="28dp"
  66. app:layout_constraintBottom_toBottomOf="@+id/ip"
  67. android:layout_marginEnd="28dp"
  68. app:layout_constraintRight_toRightOf="parent"
  69. android:layout_marginTop="16dp"
  70. tools:layout_constraintLeft_creator="1"
  71. android:layout_marginBottom="21dp"
  72. app:layout_constraintLeft_toLeftOf="parent"
  73. app:layout_constraintTop_toTopOf="parent">
  74.  
  75. <TextView
  76. android:id="@+id/show"
  77. android:layout_width="wrap_content"
  78. android:layout_height="wrap_content"
  79. app:layout_constraintBottom_toBottomOf="parent"
  80. app:layout_constraintHorizontal_bias="0.151"
  81. app:layout_constraintLeft_toLeftOf="parent"
  82. app:layout_constraintRight_toRightOf="parent"
  83. app:layout_constraintTop_toTopOf="parent"
  84. app:layout_constraintVertical_bias="0.105" />
  85. </ScrollView>
  86.  
  87. </android.support.constraint.ConstraintLayout>
 

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

为了避免UI线程被阻塞,该程序将建立网络连接、与网络服务器通信等工作都交给Client thread线程完成,代码47行处启动 ClientThread线程。

由于 Android不允许子线程访问界面组件,因此上面的程序定义了一个 Handler来处理来自线程的消息,如程序中38~45行代码所示。
  Client thread子线程负责建立与远程服务器的连接,并负责与远程服务器通信,读到数据之后便通过 Handler对象发送一条消息;当 Client thread子线程收到UI线程发送过来的消息(消息携带了用户输入的内容)后,还负责将用户输入的内容发送给远程服务器。该子线程的代码如上ClientThread代码。

ClientThread线程的功能是不断的获取Soket输出流中的内容,当读到Socket输入流中内容后,便通过Handler对象发送一条消息,消息负责携带读到的数据。该线程还负责读取UI线程发送的消息,接收消息后,该子线程负责将消息中携带的数据发送到远程服务器。

ClientThread子线程中Looper学习

手机服务器项目代码下载:http://download.csdn.net/download/qq_36726507/10183204

手机客户端项目下载:http://download.csdn.net/download/qq_36726507/10183212

Android通过手机搭建服务器,WIFI建立热点实现C/S聊天室通信功能的更多相关文章

  1. Android 监测手机联网状态 wifi、移动数据流量、无联网状态

    手机当完成联网时会发送一个广播,我们只要创建一个广播接收者即可,代码如下: package com.example.NetworkChangeReceiver2; import android.con ...

  2. Android 基于Socket的聊天室(一)

    Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信. Client A ...

  3. Android简单的聊天室开发(client与server沟通)

    请尊重他人的劳动成果.转载请注明出处:Android开发之简单的聊天室(client与server进行通信) 1. 预备知识:Tcp/IP协议与Socket TCP/IP 是Transmission ...

  4. ubuntu14.10建立热点wifi分享给手机

    http://jingyan.baidu.com/article/363872ecd8f35d6e4ba16f97.html ubuntu14.10建立热点wifi分享给手机

  5. 树莓派搭建钓鱼wifi热点

    我们连接的公共wifi其实是非常不安全的网络,骇客可以利用wifi路由设备进行中间人攻击,劫持DNS伪造钓鱼网站.接下来我会做个简单的实验,伪造中国电信的路由ChinaNet并发射出热点wifi等待别 ...

  6. 手机连上wifi热点后自动弹窗的功能

    使用buildroot编译bind DNS服务器 用buildroot来制作文件系统很方便,编译出来的文件系统是直接可用的,不用添加脚本等麻烦的工作,很多的库和app都可以直接添加到文件系统里边,如常 ...

  7. android 代码设置、打开wifi热点及热点的连接

    用过快牙的朋友应该知道它们在两天设备之间传输文件的时候使用的是wifi热点,然后另一台便连接这个热点再进行传输.快牙传输速度惊人应该跟它的这种机制有关系吧.不知道它的搜索机制是怎样的,但我想应该可以通 ...

  8. android 代码设置、打开wifi热点及热点的连接(转)

      用过快牙的朋友应该知道它们在两天设备之间传输文件的时候使用的是wifi热点,然后另一台便连接这个热点再进行传输.快牙传输速度惊人应该跟它的这种机制有关系吧.不知道它的搜索机制是怎样的,但我想应该可 ...

  9. 利用安卓手机搭建WEB服务器

    背景介绍 Android是一种基于Linux的自由及开放源代码的操作系统 所以是用安卓来搭建服务器是完全可行的.接下来将教大家如何利用AndroPHP和Feel FTP(或者其他FTP管理器)来在安卓 ...

随机推荐

  1. [ZZ] UIUC同学Jia-Bin Huang收集的计算机视觉代码合集

    UIUC同学Jia-Bin Huang收集的计算机视觉代码合集 http://blog.sina.com.cn/s/blog_4a1853330100zwgm.htmlv UIUC的Jia-Bin H ...

  2. PCL中Sample_consensus分割支持的几何模型

    随机采样一致分割法,从点云中分割出线.面等几何元素 #include <pcl/sample_consensus/method_types.h> #include <pcl/samp ...

  3. JavaScript常见的代码精简

    1.&& callback && callback() 等价于: if(callback){ callback(); } 表达的意思: 先判断 callback 是不是 ...

  4. 铁板纹理 Base Shape

    软件:Substance Designer 2017.1.2 最近正在根据官方的教程,学习Metal Rust纹理的制作.这篇文章仅记录Base Shape的制作方法. Base Shape最终渲染效 ...

  5. 涂抹mysql笔记-安装mysql

    1.mysql安装:(1)RPM安装:rpm -ivh xxx 建议安装三个:MySQL-server-VERSION.PLATFORM-cpu.rpmMySQL-client-VERSION.PLA ...

  6. html5 + shiro

    偶然与巧合 舞动了蝶翼 谁的心头风起 前赴而后继 万千人追寻 荒漠唯一菩提 似擦肩相遇 或擦肩而去 命运犹如险棋 无数时间线 无数可能性 终于交织向你

  7. VS 代码片段集

    片段1:用于线程执行代码,耗时操作时加载Loging; <?xml version="1.0" encoding="utf-8"?> <Cod ...

  8. File mapping

    文件映射的三个功能: 1.File mapping allows the process to use both random input and output (I/O) and sequentia ...

  9. gentoo Cataclysm - Dark Days Ahead

    gentoo 中安装 Cataclysm - Dark Days Ahead,使用web 下载稳定版的安装包,使用 tar 进行解压. 安装需要共享库:sdl2-mixer, 未完待续

  10. airbnb 开源reAir 工具 用法及源码解析(一)

    reAir 有批量复制与增量复制功能 今天我们先来看看批量复制功能 批量复制使用方式: cd reair ./gradlew shadowjar -p main -x test # 如果是本地tabl ...