【移动开发】WIFI热点通信(二)

摘要:
如上所述,一旦热点成功创建,GameServer将在当前手机的后台自动创建。同时,线程侦听端口将打开并等待连接。当其他玩家成功连接到热点时,每个手机客户端后台都会创建一个用于发送和接收消息的独立套接字。此设计的目的是在其他手机连接到指定的WIFI热点时与后台服务器连接,即实现TCP/IP通信的准备。

相信大家在上一篇中已经了解了Android中WIFI热点通信的相关操作知识(http://smallwoniu.blog.51cto.com/3911954/1536126),今天我们将在上一篇代码基础之上进行Socket编程,实现一个简单的多人聊天室功能,以达到热点网络上的通信目的。

    首先,我们先来看一张最终效果图:

wKioL1PnXjSTfQyFAADMP7HFqUc438.jpg<=======>wKiom1PnXRuwN6UfAAEyJEArh20439.jpg

(说明:由于目前作服务器端的手机,只是实现了数据的接收和转发,自己发送的数据并未显示到自己的界面上,还需大家完善。。。)

 

一.框架搭建

在上一章的代码基础上,新增加了四个类:

    GameServer:服务器端实现。

    SocketClient:客户端实现类。

    ChatAdapter:聊天列表适配器。

    ChatMessage:聊天信息实体。

    GroupChatActivity:聊天室Acitivity。

1.1.相关类图

在热点连接成功后,开始聊天通信过程,服务器端与客户端的类实现如下图所示:

wKioL1PAkQzi-v20AAP9qevcyE8708.jpg

1.2.说明:

服务端:套接字GameServer,端口和套接字监听函数beginListen(),接收数据的函数serverAcceptClientMsg(),发送数据的函数sendMsgToAllCLients,以及网络通讯流BufferedReader。

客户端:套接字SocketClient,套接字连接函数startConnServer(),接收数据的函数acceptGameServerMsg(),发送数据的函数sendMsg()。

前面提到过创建热点成功后,会自动在当前手机后台创建GameServer,同时开启线程监听端口并等待连接,当其余玩家成功连接上热点后,每个手机客户端后台对应会创建一个独立的Socket,用于发送和接收消息。在客户端中通过client.getInputStream()接收数数据,ClientMsgListener.handlerHotMsg(getSMsg)将数据反映到UI界面上,最终实现了客户端接收服务器端数据刷新UI界面的功能。

二.通信模块

 

    2.1.服务器端

由于软件的通信载体是在手机上,所以在创建完成热点之后,在后台也同时创建了游戏的服务器,开启了监听PORT线程,等待其他客户端连接。这样设计的目的是为了在当有其他手机端连接上指定WIFI热点时就与后台服务器端进行了连接,即实现了TCP/IP通讯前期准备。主要业务设计如图所示:

wKiom1PAjpniy6N4AACe8Ur9PJo749.jpg

核心代码:

  •  beginListenandAcceptMsg()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 /** init server to listen **/
    public void beginListenandAcceptMsg() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try // init server
                    mServerSocket = new ServerSocket();
                    mServerSocket.setReuseAddress(true);
                    InetSocketAddress address = new InetSocketAddress(mPort);
                    mServerSocket.bind(address);
                    mServerMsgListener.handlerHotMsg(Global.INT_SERVER_SUCCESS);
                    Log.i(TAG, "server  =" + mServerSocket);
                catch (SocketException e) {
                    e.printStackTrace();
                catch (IOException e) {
                    e.printStackTrace();
                }
                //server accept from socket msg
                if(mServerSocket != null) {
                    while(onGoinglistner) {
                        try {
                            Socket socket = mServerSocket.accept();
                            if(socket != null) {
                                if(!socketQueue.contains(socket)) {
                                    socketQueue.add(socket);
                                    count++; //记录连接人数
                                }
                                Log.i(TAG, "接收客户端消息" + socket);
                                serverAcceptClientMsg(socket);
                            }
                        catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();
    }
  • serverAcceptClientMsg()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 /**
     * accept from socket msg
     * @param socket
     */
    private void serverAcceptClientMsg(final Socket socket) {
        new Thread(new Runnable(){
            @Override
            public void run() {
                while(!socket.isClosed()) {
                    try {
                    //此处可以根据连接的客户端数量count做一些数据分发等操作。
                        //接收客户端消息
                    in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                        String str = in.readLine();
                        if(str == null || str.equals("")) {
                            break;
                        }
                        Log.i(TAG, "client" + socket + "str =" + str);
                    mServerMsgListener.handlerHotMsg(str);
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
  • sendMsg()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**send msg to the socket**/
    public void sendMsg(Socket client, String chatMsg) {
        Log.i(TAG, "into sendMsg(final Socket client,final ChatMessage msg) msg = " + chatMsg);
        PrintWriter out = null;
        if (client.isConnected()) {
            if (!client.isOutputShutdown()) {
                try {
                    out = new PrintWriter(client.getOutputStream());
                    out.println(chatMsg);
                    out.flush();
                    Log.i(TAG, "into sendMsg(final Socket client,final ChatMessage msg) msg = " + chatMsg + " success!");
                catch (IOException e) {
                    e.printStackTrace();
                    Log.d(TAG, "into sendMsg(final Socket client,final ChatMessage msg) fail!");
                }
            }
        }
        Log.i(TAG, "out sendMsg(final Socket client,final ChatMessage msg) msg = " + chatMsg);
    }

    2.2.客户端

        这里的客户端建立指的是当其他手机在该软件的WIFI管理界面上,点击可用WIFI列表中指定的WIFI进行连接操作,连接成功后,会在后台创建客户端,与服务器相连。主要业务设计如图所示:

wKioL1PAjsfhwDdxAACapz8mr18277.jpg

核心代码:

  • connServerandAcceptMsg()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**after hot pot created and connected successful , start connect GameServer**/
    public void connServerandAcceptMsg() {
        Log.i(TAG, "into connectServer()");
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    client = new Socket(site, port);
                    Log.i(TAG, "Client is created! site:" + site + " port:" + port);
                     
                    //callback
                    mClientMsgListener.handlerHotMsg(Global.INT_CLIENT_SUCCESS);
                     
                    //accept msg from GameServer
                    acceptGameServerMsg();
                catch (UnknownHostException e) {
                    e.printStackTrace();
                    mClientMsgListener.handlerErorMsg(Global.INT_CLIENT_FAIL);
                catch (IOException e) {
                    e.printStackTrace();
                    mClientMsgListener.handlerErorMsg(Global.INT_CLIENT_FAIL);
                }
            }
        }).start();
        Log.i(TAG, "out connectServer()");
    }
  • acceptGameServerMsg()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**accept msg from GameServer**/
    private void acceptGameServerMsg() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(onGoinglistner){
                    if(client != null && client.isConnected()) {
                        if(!client.isInputShutdown()) {
                            try {
                                in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                                String getSMsg = in.readLine();
                                Log.i(TAG, "into acceptMsg()  SMsg =" + getSMsg);
                                if(getSMsg != null || !getSMsg.equals("")) {
                                    //callback
                                mClientMsgListener.handlerHotMsg(getSMsg);
                                }
                            catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }).start();
    }
  • sendMsg()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**send msg to GameServer**/
    public String sendMsg(final String chatMsg) {
        Log.i(TAG, "into sendMsgsendMsg(final ChatMessage msg)  msg =" + chatMsg);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (client != null && client.isConnected()) {
                        if (!client.isOutputShutdown()) {
                            PrintWriter out = new PrintWriter(client.getOutputStream());
                            out.println(chatMsg);
                            // out.println(JsonUtil.obj2Str(msg));
                            Log.i(TAG, "成功发送msg =" + chatMsg);
                            out.flush();
                        }
                    }
                catch (IOException e) {
                    e.printStackTrace();
                    Log.d(TAG, "client snedMsg error!");
                }
            }
        }).start();
        return "";
    }

以上两大部分为Socket编程部分,为了能够将数据反映到UI 前台,这里我们将每次线程接收到的数据先以接口回调方法( mClientMsgListener.handlerHotMsg(getSMsg);)的形式传递,在其对应的方法中再利用Handler消息机制将数据发送到各自对应的Handler中,最后根据逻辑将其反映到UI上,以上就是代码的大体流程。

2.3.通信过程

下载过完整代码的朋友就会发现代码中许多重要的方法中我加入了Log,目的就是为了方便自己能够更加清晰的了解整个代码的流程,当然大家也可以在此基础上进行不断的修改和完善

  • 点击创建热点按钮:

wKioL1Pg7HrBZM-zAAQ-D1LavDQ087.jpg

 

 

 

 

 

 

 

 

 

 

  • 点击搜索热点按钮:

wKioL1Pg7M3BagyFAARLtpu0XOU567.jpg



 

 

 

 

 

 

 

  • 点击列表“WIFI-TEST”进行连接

wKiom1Pg7VuTqR64AAQ9q0ajUR4223.jpg

 

三.总结

 

1.此案例由于是从本人毕业设计中扒下来的,可能现在有些地方代码框架设计的不是很合理,如:GroupChatActivity就是为了方便实现聊天功能后添加的,大家在学习完之后可以在Activity跳转时的基础上,进一步按照自己的逻辑来实现一些东西。

    2.UI如何更新?

     服务器端只是实现数据转发,未对自己发送数据进行显示,了解了整个代码的同学可能已经发现不论是Server还是Client端,在接收到数据之后,我们通过各自的监听器(mServerMsgListener,mClientMsgListener)来回调对应的方法(handlerHotMsg,handlerErrorMsg),在方法中我们将数据添加msg.obj中,最终以消息传递的方式发送到各自对应的handler中(clientHandler,serverHandler),在那里我们就可以根据数据来更新界面。

3.题外话:

     要是有人对热点通信特别感兴趣,想在此的基础之上开发小游戏,前台游戏绘制界面就不用多说了,我主要想说的是后台数据部分,最好能给所有操作制定了一系列对应的数据规则,如:出牌操作:在传输的数据串前面加上规则字符---->“《#CARD》+数据段”,之后作为整体发送出去,这样的话,接收方在接收到数据后可以方便的更新UI,实现对应的游戏动画。(个人经验,仅供参考)

源码下载:http://down.51cto.com/data/1856373

本文出自 “狂奔的蜗牛” 博客,请务必保留此出处http://smallwoniu.blog.51cto.com/3911954/1538298

免责声明:文章转载自《【移动开发】WIFI热点通信(二)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇重启 Openstack Swift 服务【Linux C中文函数手册】之 目录操作函数下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

蓝牙(Bluetooth)基本介绍(转载)

 •蓝牙(Bluetooth)是一种短距离的无线数据与语音通信的开放性全球规范。 –蓝牙技术能为个人和商业的移动设备的无线连接带来一次革命,它不需要电缆,能通过短距离的无线链路使得用户将多种设备方便快速连接起来进行无缝的语音和数据通信。 –蓝牙区别于其它无线技术的一个典型特征是它能基于各设备各自的功能提供“联合使用模型”。 –通过数据访问点DAP,蓝牙还可...

C# Socket

------------------------------------------------------------------------------------------------------------------------------------------- using System;using System.Text;using Sy...

Socket编程:UDP和TCP概论及案例

网络编程的三要素: 1.IP地址 2.端口 3.协议 什么是Socket? Socket就是通信链路的端点称"套接词". 基于TCP协议的Socket网络通信:用来实现双向安全连接网络通信Socket通信模型:进行网络通信时,Socket需要借助数据流来完成数据的传递工作 Socket网络编程一般可以分成如下步骤进行,如图所示: UDP、TCP概述:...

4G EPS 的架构模型

目录 文章目录 目录 前文列表 EPS 的架构 EPS 的架构模型 E-UTRAN UE eNodeB EPC MME(移动性管理) SGW(本地移动性锚点) PGW(业务锚点) HSS(用户认证及鉴权中心) PCRF(计费规则与策略) EPS 运行原理 上行传输 下行传输 前文列表 《4G EPS 第四代移动通信系统》 EPS 的架构...

Qt学习 之 Socket通信

近期写大作业用到Qt的Socket部分。网上关于这部分的资料都太过复杂,如今总结一下一些简单的应用。有机会能够给大家讲讲用Socket传送文件的代码。 这里主要解说怎样实现TCP和UDP的简单通信。 socket简单介绍 在LINUX下进行网络编程。我们能够使用LINUX提供的统一的套接字接口。 可是这样的方法牵涉到太多的结构体,比方IP地址,端口转换...

开网页自动进入路由器设置界面的解决办法(腾达路由器)

腾达路由器设置完毕后,开网页自动进入路由器设置界面,并且WAN口状态显示连接中 问题1:显示“连接中”怎么办? 原因1:路由器WAN口的上网方式选择错误。 解决方法:进到路由器设置页面,点击“高级设置”-“WAN口设置”选择正确的上网方式. 原因2:宽带的账号和密码输错了或者没有区分大小写 解决方法:输入正确的宽带帐号和密码,注意区分帐号密码大小写。 原...