zoukankan      html  css  js  c++  java
  • Android通过手机搭建服务器,WIFI建立热点实现C/S聊天室通信功能

    应用效果图:

                                                  

                                                                                                  客户端                                                                                                          服务器端

     

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

     一、服务器端

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

    所需权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <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 + "
    ").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 }
    View Code
     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 }
    View Code

      上面服务器线程类不断读取客户端数据,程序使用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("
    " + 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() + "
    ").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>
    View Code
     

      当用户单击该程序界面中的“发送”按钮后,程序将会把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

  • 相关阅读:
    <trim>: prefix+prefixOverrides+suffix+suffixOverrides
    时间js
    form表单中name和id区别
    SQL 递归
    表单中Readonly和Disabled的区别
    jquery select取值,赋值操作
    ie11
    mongodb 学习资料
    H5 限制input只能输入数字
    http头部解释
  • 原文地址:https://www.cnblogs.com/jxust-jiege666/p/8169057.html
Copyright © 2011-2022 走看看