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

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


<=======>

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


一.框架搭建

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

GameServer:服务器端实现。

SocketClient:客户端实现类。

ChatAdapter:聊天列表适配器。

ChatMessage:聊天信息实体。

GroupChatActivity:聊天室Acitivity。

1.1.相关类图

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

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通讯前期准备。主要业务设计如图所示:

核心代码:

beginListenandAcceptMsg()

/**initservertolisten**/publicvoidbeginListenandAcceptMsg(){newThread(newRunnable(){@Overridepublicvoidrun(){try{//initservermServerSocket=newServerSocket();mServerSocket.setReuseAddress(true);InetSocketAddressaddress=newInetSocketAddress(mPort);mServerSocket.bind(address);mServerMsgListener.handlerHotMsg(Global.INT_SERVER_SUCCESS);Log.i(TAG,"server="+mServerSocket);}catch(SocketExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}//serveracceptfromsocketmsgif(mServerSocket!=null){while(onGoinglistner){try{Socketsocket=mServerSocket.accept();if(socket!=null){if(!socketQueue.contains(socket)){socketQueue.add(socket);count++;//记录连接人数}Log.i(TAG,"接收客户端消息"+socket);serverAcceptClientMsg(socket);}}catch(IOExceptione){e.printStackTrace();}}}}}).start();}

serverAcceptClientMsg()

/***acceptfromsocketmsg*@paramsocket*/privatevoidserverAcceptClientMsg(finalSocketsocket){newThread(newRunnable(){@Overridepublicvoidrun(){while(!socket.isClosed()){try{//此处可以根据连接的客户端数量count做一些数据分发等操作。//接收客户端消息in=newBufferedReader(newInputStreamReader(socket.getInputStream(),"UTF-8"));Stringstr=in.readLine();if(str==null||str.equals("")){break;}Log.i(TAG,"client"+socket+"str="+str);mServerMsgListener.handlerHotMsg(str);}catch(Exceptione){e.printStackTrace();}}}}).start();}

sendMsg()

/**sendmsgtothesocket**/publicvoidsendMsg(Socketclient,StringchatMsg){Log.i(TAG,"intosendMsg(finalSocketclient,finalChatMessagemsg)msg="+chatMsg);PrintWriterout=null;if(client.isConnected()){if(!client.isOutputShutdown()){try{out=newPrintWriter(client.getOutputStream());out.println(chatMsg);out.flush();Log.i(TAG,"intosendMsg(finalSocketclient,finalChatMessagemsg)msg="+chatMsg+"success!");}catch(IOExceptione){e.printStackTrace();Log.d(TAG,"intosendMsg(finalSocketclient,finalChatMessagemsg)fail!");}}}Log.i(TAG,"outsendMsg(finalSocketclient,finalChatMessagemsg)msg="+chatMsg);}


2.2.客户端

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

核心代码:

connServerandAcceptMsg()

/**afterhotpotcreatedandconnectedsuccessful,startconnectGameServer**/publicvoidconnServerandAcceptMsg(){Log.i(TAG,"intoconnectServer()");newThread(newRunnable(){@Overridepublicvoidrun(){try{client=newSocket(site,port);Log.i(TAG,"Clientiscreated!site:"+site+"port:"+port);//callbackmClientMsgListener.handlerHotMsg(Global.INT_CLIENT_SUCCESS);//acceptmsgfromGameServeracceptGameServerMsg();}catch(UnknownHostExceptione){e.printStackTrace();mClientMsgListener.handlerErorMsg(Global.INT_CLIENT_FAIL);}catch(IOExceptione){e.printStackTrace();mClientMsgListener.handlerErorMsg(Global.INT_CLIENT_FAIL);}}}).start();Log.i(TAG,"outconnectServer()");}

acceptGameServerMsg()

/**acceptmsgfromGameServer**/privatevoidacceptGameServerMsg(){newThread(newRunnable(){@Overridepublicvoidrun(){while(onGoinglistner){if(client!=null&&client.isConnected()){if(!client.isInputShutdown()){try{in=newBufferedReader(newInputStreamReader(client.getInputStream()));StringgetSMsg=in.readLine();Log.i(TAG,"intoacceptMsg()SMsg="+getSMsg);if(getSMsg!=null||!getSMsg.equals("")){//callbackmClientMsgListener.handlerHotMsg(getSMsg);}}catch(IOExceptione){e.printStackTrace();}}}}}}).start();}

sendMsg()

/**sendmsgtoGameServer**/publicStringsendMsg(finalStringchatMsg){Log.i(TAG,"intosendMsgsendMsg(finalChatMessagemsg)msg="+chatMsg);newThread(newRunnable(){@Overridepublicvoidrun(){try{if(client!=null&&client.isConnected()){if(!client.isOutputShutdown()){PrintWriterout=newPrintWriter(client.getOutputStream());out.println(chatMsg);//out.println(JsonUtil.obj2Str(msg));Log.i(TAG,"成功发送msg="+chatMsg);out.flush();}}}catch(IOExceptione){e.printStackTrace();Log.d(TAG,"clientsnedMsgerror!");}}}).start();return"";}

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


2.3.通信过程

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

点击创建热点按钮:












点击搜索热点按钮:













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


三.总结


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