Android开发笔记(一百一十一)聊天室中的Socket通信

本文详细介绍了Android中利用Socket实现聊天室通信的过程,包括Socket、ServerSocket和InetAddress的基本概念,以及聊天室应用的实现原理和代码示例,分为app端和服务端两部分进行阐述。
摘要由CSDN通过智能技术生成

Socket通信

基本概念

对于程序开发来说,网络通信的基础就是Socket,但因为是基础,所以用起来不容易,今天我们就来谈谈Socket通信。计算机网络有个大名鼎鼎的TCP/IP协议,普通用户在电脑上设置本地连接的ip时,便经常看到下图的弹窗,注意红框部分已经很好地描述了TCP/IP协议的作用。



TCP/IP是个协议组,它分为三个层次:网络层、传输层和应用层:
网络层包括:IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
传输层包括:TCP协议、UDP协议。
应用层包括:HTTP、FTP、TELNET、SMTP、DNS等协议。
之前我们提到的网络编程,其实都是应用层方面的http或ftp编程;而socket属于传输层的技术,它的api实现TCP协议后即可用于http通信,实现UDP协议后即可用于ftp通信,当然也可以直接在底层进行点对点通信,比如即时通信软件(QQ、微信)这样就是。


扯远了,言归正传,java的socket编程主要使用Socket和ServerSocket两个类,下面是相关类的使用说明。


Socket

Socket是最常用的,客户端和服务端都要用到,它描述了两边对套接字(即Socket)处理的一般行为,主要方法说明如下:
connect : 连接指定ip和端口。该方法用于客户端连接服务端。
getInputStream : 获取输入流。即自己收到对方发过来的数据。
getOutputStream : 获取输入流。即自己向对方发送的数据。
getInetAddress : 获取网络地址对象。该对象是一个InetAddress实例。
isConnected : 判断socket是否连上。
isClosed : 判断socket是否关闭。
close : 关闭socket。


ServerSocket

ServerSocket仅用于服务端,它在运行时不停地侦听指定端口,主要方法说明如下:
构造函数 : 指定侦听哪个端口。
accept : 开始接收客户端的连接。有客户端连上时就返回一个Socket对象,若要持续侦听连接,得在循环中调用该函数。
getInetAddress : 获取网络地址对象。该对象是一个InetAddress实例。
isClosed : 判断socket服务器是否关闭。
close : 关闭socket服务器。


InetAddress

InetAddress是对网络地址的一个封装,主要方法说明如下:
getByName : 根据主机ip/名称获取InetAddress对象。
getHostAddress : 获取主机的ip地址。
getHostName : 获取主机的名称。
isReachable : 判断该地址是否可到达,即是否连通。


聊天室应用

实现原理

Socket在app开发中主要用于聊天/即时通信,因为涉及到客户端与服务端的交互,所以流程稍微复杂。首先要划分聊天业务的功能点,以QQ为例,我们平常看到的是三种页面:登录页面、好友列表页面、聊天页面。因此,对应的Socket功能也分为三类:
登录/注销:登录操作对应建立Socket连接,而注销操作对应断开Socket连接。
获取好友列表:与Socket有关的是获取当前在线的好友列表,客户端到服务端查询当前已建立Socket连接的好友列表。
发送消息/接收消息:发送/接收消息对应的是Socket的数据传输,发送消息操作是客户端A向服务端发送Socket数据,接收消息操作是服务端将收到的A消息向客户端B发送Socket数据。


其次在app端需要实现以下功能:
1、至少三个页面:登录页面、好友列表页面、聊天页面;
2、一个用于Socket通信的线程。由于在app运行过程中都要保持Socket连接,因此该Socket线程要放在自定义的Application类中。
3、页面向Socket线程发送消息的机制,用于登录请求、注销请求、获取好友列表请求、发送消息等等。主线程与子线程通信,我们这里采用Handler+Message机制来处理,有关该机制的使用说明参见《 Android开发笔记(四十八)Thread类实现多线程》。
4、Socket线程向页面发送消息的机制,用于返回好友列表、接收消息等等。因为返回消息会分发到不同的页面,采用Handler机制有困难,所以这里我们采用Broadcast广播来处理,在好友列表页面和聊天页面各注册一个广播接收器,用于根据服务器返回数据刷新UI。有关Broadcast及其接收器的使用说明参见《 Android开发笔记(四十二)Broadcast的生命周期》。


然后在服务端启动Socket服务器,要实现的功能有:
1、定义一个Socket连接的队列,用于保存当前连上的Socket请求;
2、循环侦听指定端口,一旦有新连接进来,则将该连接加入Socket队列,并启动新线程为该连接服务;
3、每个服务线程持续从Socket中读取客户端发过来的数据,并对不同请求做相应的处理:
a、如果是登录请求,则标识该Socket连接的用户昵称、设备编号、登录时间等信息;
b、如果是注销请求,则断开Socket连接,并从Socket队列中移除该连接;
c、如果是获取好友列表请求,则遍历Socket队列,封装好友列表数据并返回;
d、如果是发送消息请求,则根据好友的设备编号到Socket队列中查找对应的Socket连接,并向该连接返回消息内容;


最后还要定义一下服务端与客户端之间传输消息的格式,按惯例消息包分为包头与包体两块,包头用于标识操作类型、操作对象、操作时间等基本要素,而包体用于存放具体的消息内容(如好友列表、消息文本等等)。demo工程为简单起见,就不用xml或json等标准格式,直接用分隔符划分包头与包体,以及包头内部的各元素。


效果截图

博主在测试时,模拟器上开了一个app,登录名称是“在水一方”,真机上开了一个app,登录名称是“振兴中华”,两个app连的都是电脑上的Socket服务,从而模拟真实的聊天室环境。登录页面与好友列表页面比较简单,就不再截图了,截的都是聊天窗口页面。为了做得更逼真,中间消息窗口采用对方消息靠左对齐,我方消息靠右对齐的布局,并给双方消息着不同的背景色。具体截图如下,左侧图片是真机截图,右侧图片是模拟器截图。





代码示例

app端

几个注意点:
1、自定义Application类需要采用单例模式,确保Socket线程的唯一性,详细原因参见《 Android开发笔记(八十九)单例模式》。
2、Socket数据包不可直接用换行符“\n”做分隔符,因为在Socket通信中,换行符表示该数据包结束了,所以加了一个换行符,原来一个数据包就变成了两个数据包。正因如此,用户聊天的消息文本中若有换行符,则要先进行转义后才能发给Socket传输。
3、如果广播接收器在代码中动态注册,则不会收到Socket线程发出的广播消息;只有在AndroidManifest.xm
评论 104
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值