在Android中进行Socket编程

【此处应该有关于Socket编程和网络模型的介绍】

Java中常用的有关网络的类

针对不同的网络通信层次,Java给我们提供的网络功能有四大类:

InetAddress: 用于标识网络上的硬件资源
URL: 统一资源定位符,通过URL可以直接读取或者写入网络上的数据
Socket和ServerSocket: 使用TCP协议实现网络通信的Socket相关的类
Datagram: 使用UDP协议,将数据保存在数据报中,通过网络进行通信

Android 中的Socket编程模型

安卓中的Socket编程模型
在android中,实现Socket通信需要四个步骤:
1. 创建ServerSocket 或者Socket, 前者是作为服务器所需要的,而后者是作为客户端所需要的。在创建相应的Socket的时候,需要制定相应的地址和端口(或者侦听端口)
2. 打开Socket对应的输入/输出流
3. 按照协议对输入/输出流进行操作
4. 按照顺序关闭输入/输出流,以及Socket

Socket服务端

如果需要在安卓上建立服务端,那么我们需要这样做:
1. 建立ServerSocket,指定要侦听的IP地址和端口(注意IP地址不能是Localhost对应的地址,很多时候localhost对应的地址是127.0.0.1,有时会侦听不到)
2. 调用accept()方法来监听
3. 连接建立后,通过输入流读取客户端发来的消息;
4. 通过输出流响应客户端;
5. 按照顺序关闭输入/输出流,以及Socket来结束通信
示例代码:

public void DoServer(string IP, int port)
{
    ServerSocket serverSocket = new ServerSocket(port); // 创建ServerSocket
    Socket socket = serverSocket.accept() // 侦听端口,接受请求
    InputStream inStream = socket.getInputStream(); //创建InputStream
    OutputStream outStream = socket.getOutputStream(); //创建OutputStream
    InputStreamReader inputStreamReader = new InputStreamReader(inStream, "UTF-8"); //创建InputStreamReader,并配置编码,此处的编码应该和客户端发送的时候的编码一致,否则会出现乱码
    BufferedReader bufReader = new BufferedReader(inputStreamReader); //创建缓冲读取,这样我们就可以从缓冲区读取数据,而不用担心数据丢失
    while(string data = bufReader.readLine() != null) //循环读取缓冲区
    {
        // do something you want
        System.out.println(data);
    }
    socket.shutdown();
    socket.close();
}

Socket客户端

在客户端,需要做的事情就会少一些(不用侦听),一共需要大概四步:
1. 创建Socket对象,说明服务端的IP和端口号;
2. 通过输出流向服务端发送信;
3. 通过输入流获取服务端发来的消息;
4. 关闭有关的Socket;
示例代码:

public void DoClient(string IP, int port)
{
    Socket socket = new Socket(IP, port); // 创建Socket,指明服务端的IP和端口
    OutputStream outStream = socket.getOutputStream(); // 从socket创建输出流
    PrintWriter printWriter = new PrintWriter(outStream); //创建流写入器
    // do something with printWriter
    printWriter.write("Hello, server!");
    printWriter.flush(); // 这一步是必要的,有时向printWriter写入的数据不会立即发送,这时我们就需要flush一下
    socket.shutdownOutput(); //关闭输出流
    socket.close();

Note

  • 需要注意的一点是,在Android中不允许在主线程中进行网络操作,如果这样做了,那么程序会在进行网络链接的时候崩溃/闪退/Crash,崩溃的原因是抛出了“NetworkOnMainThreadException”异常参见此处

    The explanation as to why this occurs is well documented on the Android developer’s site:

    A NetworkOnMainThreadException is thrown when an application attempts to perform a networking operation on its main thread. This is only thrown for applications targeting the Honeycomb SDK or higher. Applications targeting earlier SDK versions are allowed to do networking on their main event loop threads, but it’s heavily discouraged. Some examples of other operations that JellyBean, ICS and HoneyComb and won’t allow you to perform on the UI thread are:

    1. Opening a Socket connection (i.e. new Socket()).
    2. HTTP requests (i.e. HTTPClient and HTTPUrlConnection).
    3. Attempting to connect to a remote MySQL database.
    4. Downloading a file (i.e. Downloader.downloadFile()).

    If you are attempting to perform any of these operations on the UI thread, you must wrap them in a worker thread. The easiest way to do this is to use of an AsyncTask, which allows you to perform asynchronous work on your user interface. An AsyncTask will perform the blocking operations in a worker thread and will publish the results on the UI thread, without requiring you to handle threads and/or handlers yourself.