2.5 Socket网络通信

伴随着移动互联网的高速增长,智能手机上的游戏娱乐应用也逐步由单机向网络化方向发展。因此,对于Android开发人员来说,开发具有网络功能的应用程序成为了一项必备的技能。本节将向读者介绍网络开发中非常重要的一项技术,Socket(网络套接字)应用开发。

2.5.1 Socket开发基本知识

网络应用程序的开发也有很多不同的架构与模式,常见的有B/S、C/S等,Socket主要是用于进行传统的C/S模式应用的开发。C/S模式中主要需要开发两个端,服务端与客户端,其基本的工作原理如图2-24所示。

从图2-24中可以看出,C/S模式通信的基本过程如下。

▲图2-24 C/S模式通信的基本原理

(1)首先启动服务器,监听指定端口,等待接收客户端的连接请求。

(2)客户端请求连接到服务器的指定端口。

(3)服务器收到客户端的连接请求,建立连接。

(4)客户端和服务器同时各打开一个输入流和输出流,客户端的输出流与服务器的输入流连接,服务器的输出流与客户端的输入流连接。

(5)客户端与服务器端通过输入输出流进行双向的消息通信。

(6)当通信完毕后,客户端和服务器同时关闭本次连接。

由于Android应用程序是使用Java进行开发,因此,在Android平台下开发基于Socket的C/S模式程序非常简单,直接使用Java中的Socket与ServerSocket类即可。这与传统Java的网络套接字开发几乎没有区别,对于熟悉Java的大部分读者来说非常简单。

2.5.2 服务器端

上一小节简单介绍了基于Socket的C/S模式网络程序的基本工作原理,从本小节开始将向读者介绍一个基于Socket的简单案例。首先介绍服务端的开发,由于本案例比较简单,所以仅有一个类Sample2_8_Server,其代码如下。

代码位置:见随书中源代码/第2章/Sample2_8_Server/src/com/bn/Sample2_8_Server目录下的Sample2_8_Server.java。

      1     package com.bn.Sample2_8_Server;                        //声明包名
      2     import java.io.DataInputStream;                         //导入相关类
      3     ……//此处省略了部分类的引入代码,读者可自行查看随书的源代码
      4     import java.net.Socket;                                   //导入相关类
      5       public  class  Sample2_8_Server{
      6           static ServerSocket sSocket;                       // ServerSocket的引用
      7           public static void main(String[] args){          //主方法
      8                       try{
      9                       sSocket=new ServerSocket(8877);       //创建ServerSocket对象
      10                      System.out.println("监听8877接口……"); //打印信息
      11                      while(true){//服务器启动后一直循环服务于不同的客户端
      12                         Socket socket=sSocket.accept();    //创建Socket对象
      13                                 DataInputStream diStream=new DataInputStream(socket. getInputStream());
      14                                 DataOutputStream  dotStream=new  DataOutputStream(socket.
                              getOutputStream());
      15                         System.out.println("客户端信息:"+diStream.readUTF());
      16                         dotStream.writeUTF("成功连接服务器端");   //写入到输出流中
      17                         diStream.close();                          //关闭输入流
      18                         dotStream.close();                         //关闭输出流
      19                         socket.close();                            //关闭Socket套接字
      20                } }catch(Exception e){                             //捕获并打印异常信息
      21                              e.printStackTrace();
      22     }  }  }

❑ 第9行创建了ServerSocket对象,监听在服务器的8877端口。

❑ 第11-20行为服务循环,服务器一旦成功启动监听在指定的端口就进入此循环,按照指定的流程服务于一个一个的客户端。本案例中的服务流程为:首先与客户端建立双向数据流,然后接收客户端的信息,最后再向客户端返回信息。

运行本案例,其运行效果如图2-25所示。

▲图2-25 对8877接口进行监听

2.5.3 客户端

开发完本案例的服务器端后,接下来在本小节中将主要介绍客户端的开发。由于本案例功能简单,因此客户端也仅有一个类Sample2_8_ ClientActivity,其代码如下。

代码位置:见随书中源代码/第2章/Sample2_8_Client/src/com/bn/Sample2_8_Client目录下的Sample2_8_ClientActivity.java。

      1     package com.bn.Sample2_8_Client;                         //声明包名
      2     ……//此处省略了部分类的引入代码,读者可自行查看随书的源代码
      3     public class Sample2_8_ClientActivity extends Activity{//创建继承Activity的主控制类
      4            @Override
      5         public void onCreate(Bundle savedInstanceState){  //重写的onCreate方法
      6                 super.onCreate(savedInstanceState);
      7             setContentView(R.layout.main);                   //跳转到主界面
      8             Button button=(Button)findViewById(R.id.button);   //获得Button按钮的引用
      9             button.setOnClickListener(                        //为Button按钮添加监听器
      10                     new  OnClickListener(){
      11                public void onClick(View v){                 //重写的onClick方法
      12                              new  Thread(){
      13                                      public  void  run(){
      14                                  connectServer();        //调用connectServer连接服务器
      15                              }}.start();
      16            }}  ); }
      17        public void connectServer(){                    //自定义的连接服务器的方法
      18          String serverIp="10.16.189.20";              //声明服务器端IP
      19             try{
      20                Socket socket=new Socket(serverIp,8877); //创建Socket套接字,发出连接请求
      21                     DataInputStream  din=new  DataInputStream(socket.getInputStream());
      22                              DataOutputStream dout=new DataOutputStream(socket.getOutputStream());
      23                              EditText  et=(EditText)this.findViewById(R.id.et);
                                                              //获得EditText输入对话框对象
      24                      String tempStr=et.getText().toString();   //获取该对话框中的信息
      25                      dout.writeUTF(tempStr);                      //将信息写入到输出流中
      26                      TextView tv=(TextView)this.findViewById(R.id.tv); //获得TextView的对象
      27                      tv.setText(din.readUTF()); //将输入流中的数据在TextView中显示
      28                      din.close();                                  //关闭输入流
      29                      dout.close();                                 //关闭输出流
      30                      socket.close();                               //关闭Socket套接字
      31             }catch(Exception  e){
      32                e.printStackTrace();                               //捕获并打印异常信息
      33     }}}

❑ 第5-16行为继承Activity后重写的onCreate方法,该方法在程序开始时执行。在该方法中首先获得“连接服务器端”按钮对象的引用,之后为该按钮添加监听器,监听器功能为connectServer方法连接服务器收发信息。

❑ 第17-33行为连接服务器收发信息的connectServer方法,其基本的工作流程为:首先向服务器发出连接请求,连接成功后建立双向数据流,然后将用户输入的信息发送给服务器端再从服务器端接收反馈信息。

提示

在创建Socket套接字对象时需要指明端口号以及服务器端的IP,这里的IP必须为真实IP,不能使用环回地址“127.0.0.1”。因此运行本案例时读者有可能需要根据自己计算机的具体情况修改服务器的IP地址,使得程序可以正常运行。同时,在开发客户端时需要在AndroidManifest.xml中声明联网权限,具体情况参见中的源代码。

运行本案例,在上面的文本框中输入需要发送的信息,然后单击“连接服务器端”按钮。若网络没有问题,则在服务器端可以收到客户端发送的信息,如图2-26所示。同时,客户端也会收到服务器的反馈信息,如图2-27所示。

▲图2-26 服务器端收到客户端信息

▲图2-27 客户端成功连接到服务器