Android客户端与本地服务器Socket通信

  现代生活中,我们的移动设备离不开网络,我们的APP也经常需要连接到网络,所以现在,在这里完成一个简单的Socket通信程序,实现Android客户端与本地的服务器通信。

  关于网络通信的知识,互联网上有很多相关的讲解,这里就不进行详细的讲解了,直接就代码进行讲解。

  需要注意的是,Android应用Socket通信时,有几点要求必须得遵守:

1. andriod客户端添加网络访问权限

  在写客户端程序时,必须要在Manifest.xml文件中添加网络访问权限,当然如果你愿意,也可以加上对WiFi状态进行查看的权限。

<uses-permission  android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

  当然,你不加这两条网络访问权限程序并不会抛出任何的异常,运行起来也并看不出有什么毛病,但是,程序结果肯定不对。之前用真机做过测试,不加这两条权限,在同一局域网内,手机是可以连接到服务器上的,只不过,在服务器端,得到连接的IP地址和服务器的IP是一样的,这可能只是偶然情况。
  如果不加这两条权限,客户端和服务器是不能进行通信的。

2.对Socket的操作放在非UI线程内进行

  在Android应用中,为了保证APP的用户体验,必须对用户的操作尽可能快的做出响应,因此在以前的Android版本中,不建议将耗时的操作放在UI线程中。到Android3.0之后的版本,则是更进一步做了限制,在非UI线程中禁止操作UI,在UI线程中禁止做响应时间长的操作,如联网操作。

   如果在UI线程中进行联网操作的话,会抛出NetworkOnMainThread异常,因此,Socket操作必须放在子线程内进行。

3.要使用正确的IP地址和端口号

  Socket通信,必须指定IP地址和端口号,需要注意的是端口号不能随便分配。

  端口号的范围是0~65535,1024一下的端口被系统分给了一些服务,慎用(建议不要使用这段范围的端口),还有一些端口被分配给了特定的服务,比如http的8080端口,其他的端口应该都没有太大问题。

  在cmd窗口执行netstat -ano命令可以看到所有端口的使用情况。

  对于IP地址,有两种情况要注意。

  如果使用真机进行调试,手机必须和电脑在同一个网段内,可以连接同一个wifi,也可以用电脑开一个无线热点,手机连上热点就行,无论哪种方式,要求手机和电脑都能上网,否则,也是无法通信的。然后,程序中新建一个Socket的时候,使用电脑的IP就可以。

  如果使用模拟器进行调试,就不必考虑那么多情况了。新建Socket时,IP地址要使用10.0.2.2,这是系统为模拟器专门分配的,不能使用127.0.0.1.至于网上说的很多,PC和模拟器通信时,要使用端口重定向的问题,我在测试的时候,没有进行端口重定向,也可以实现PC向模拟器发数据。

  好了,废话说完了,直接上代码吧。

客户端:
  在一个EditText中输入一些字符,点击Button,把这些字符发送到服务器,服务器在收到这些数据后,在将这些数据返回给客户端。(xml文件比较简单,这里就不写了)

public class MainActivity extends Activity {
    //IP地址和端口号
    public static String IP_ADDRESS = "10.0.2.2";
    public static int PORT = 2346;
    //三个控件
    EditText text = null;
    Button connect = null;
    TextView info = null;
    //handler
    Handler handler = null;
    Socket soc = null;
    DataOutputStream dos = null;
    DataInputStream dis = null;
    String messageRecv = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (EditText)findViewById(R.id.editText);
        connect = (Button)findViewById(R.id.buttonConnection);
        connect.setOnClickListener(new ConnectionListener());
        info = (TextView)findViewById(R.id.info);

        handler = new Handler(){
            @Override
            public void handleMessage(Message msg){
                super.handleMessage(msg);
                    Bundle b = msg.getData();  //获取消息中的Bundle对象
                    String str = b.getString("data");  //获取键为data的字符串的值
                    info.append(str);
                }
            }
        };
    }
    //Button的Listener
    class ConnectionListener implements OnClickListener{
        @Override
        public void onClick(View v){
            new ConnectionThread(text.getText().toString()).start();
        }
    }
    //新建一个子线程,实现socket通信
    class ConnectionThread extends Thread{
        String message = null;
        public ConnectionThread(String msg){
            message = msg;
        }
        @Override
        public void run(){
            if(soc == null){
                try {
                    //Log.d("socket","new socket");
                    soc = new Socket(IP_ADDRESS, PORT);
                    //获取socket的输入输出流
                    dis = new DataInputStream(soc.getInputStream());
                    dos = new DataOutputStream(soc.getOutputStream());
                    }catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            try{
                dos.writeUTF(message);
                dos.flush();    
                messageRecv = dis.readUTF();//如果没有收到数据,会阻塞
                Message msg = new Message();
                Bundle b = new Bundle();
                b.putString("data", messageRecv);
                msg.setData(b);
                handler.sendMessage(msg);
                }catch(){
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }
    }

服务器端
  和客户端建立连接以后,在收到的字符串前加一个“received:”,然后返回给客户端

public class Server {

    ServerSocket serverSocket = null;
    public final int port = 2346;

    public Server(){

            //输出服务器的IP地址
            try {
                InetAddress addr = InetAddress.getLocalHost();
                System.out.println("local host:"+addr);
                serverSocket = new ServerSocket(port);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }

    public void startService(){

        try {
            Socket socket = null;
            System.out.println("waiting...");
            //等待连接,每建立一个连接,就新建一个线程
            while(true){                
                socket = serverSocket.accept();//等待一个客户端的连接,在连接之前,此方法是阻塞的
                System.out.println("connect to"+socket.getInetAddress()+":"+socket.getLocalPort());
                new ConnectThread(socket).start();
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            System.out.println("IOException");
            e.printStackTrace();
        }
    }

    //向客户端发送信息
    class ConnectThread extends Thread{
        Socket socket = null;

        public ConnectThread(Socket socket){    
            super();
            this.socket = socket;
        }

        @Override
        public void run(){
            try {
                DataInputStream dis = new DataInputStream(socket.getInputStream());
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                while(true){
                    String msgRecv = dis.readUTF();
                    System.out.println("msg from client:"+msgRecv);
                    dos.writeUTF("received:"+msgRecv);
                    dos.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Server().startService();
    }

}

TCP协议,百度百科
UDP协议,百度百科

你可能感兴趣的:(Android学习笔记)