Android 客户端和 web服务器通信

时间:2021-02-20 17:58:46

本篇简单介绍Android客户端和web服务器使用socket进行通讯,向客户端发送文件的demo

socket

套接字使用TCP提供了两台计算机之间的通信机制。客户端创建一个套接字,并尝试连接服务端的嵌套字。当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。

java.net.Socket类代表一个套接字,并且java.net.ServerSocket类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送

服务端

为了实现向客户端发送文件,我们基于前面的jFinal 文件上传 来完成。


public class ServerUtils {

    private volatile static ServerUtils serverInstance;

    private static ServerSocket serverSocket;

    private ServerUtils() {
    }

    public static ServerUtils getServerInstance() {

        if (serverInstance == null) {
            synchronized (ServerUtils.class) {
                if (serverInstance == null) {
                    serverInstance = new ServerUtils();
                }
            }
        }
        return serverInstance;
    }

    public void init(int port) {
        try {
            if (serverSocket == null) {
                serverSocket = new ServerSocket(port);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static ServerSocket getServerSocket() {
        return serverSocket;
    }

     /**
       *  发送文件线程
       **/
    public static class SendThread implements Runnable {
        static Socket socket;
        File file;
        static FileInputStream fis;
        static DataOutputStream dos;

        public SendThread(File file) {
            this.file = file;
        }

        @Override
        public void run() {

            try {
                 // 监听并接受到此嵌套字的连接 (该方法会阻塞等待,直到客户端连到服务端的指定端口)
                socket = getServerSocket().accept();
              
                // 上传的模型文件
                File getFile = file;
                fis = new FileInputStream(getFile);

                // 获取嵌套字的输出流
                dos = new DataOutputStream(socket.getOutputStream());
                  // 嵌套字的输入流
                //dis = new DataInputStream(socket.getInputStream());
              
                // 模型名称和大小
                dos.writeUTF(getFile.getName());
                dos.flush();
                dos.writeLong(getFile.length());
                dos.flush();

                byte[] bytes = new byte[1024];
                int length = 0;

                while ((length = fis.read(bytes, 0, bytes.length)) != -1) {
                    dos.write(bytes, 0, length);
                    dos.flush();
                }

            } catch (IOException e) {
                e.printStackTrace();

            } finally {
                if (fis != null)
                    fis.close();
                if (dos != null)
                    dos.close();
                if (socket != null) {
                    socket.close();
                }
            }

        }
    }

}

UploadController:



private static ThreadPoolExecutor threadPool;
private static SendThread sendThread;

// jfinal 获取 Web Uploader 上传的文件 
final File getFile = getFile().getFile();

threadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

// 初始化 ServerSocket
ServerUtils.getServerInstance().init(port);

sendThread = new SendThread(getFile);

// 创建一个线程 向客户端发送文件
threadPool.submit(sendThread);

客户端



   final Socket socket = new Socket();
        final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 4,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());

        threadPool.submit(new Runnable() {
            @Override
            public void run() {

                try {
                    socket.connect(new InetSocketAddress("192.168.0.100", 6001));
                    // 获取嵌套字输入流
                    final DataInputStream dis = new DataInputStream(socket.getInputStream());

                    // 获取文件名
                    String fileName = dis.readUTF();
                    // 获取文件大小
                    final long fileLength = dis.readLong();
                    File file = new File(App.RECEIVE_PATH + File.separatorChar + fileName);
                    final FileOutputStream fos = new FileOutputStream(file);

                    threadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                int length = 0;
                                byte[] bytes = new byte[1024];

                                while ((length = dis.read(bytes, 0, bytes.length)) != -1) {
                                    fos.write(bytes, 0, length);
                                    fos.flush();

                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                    });

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

应用场景

这里只是一个最基本的上传demo,每次用户web页面上传文件,服务端都会开启一个线程来发送文件。客户端连接并接收上传的文件,文件发送结束 关闭客户端。

关于socket 优化以及深入了解

可参见Java Socket编程基础及深入讲解

NIO

上述的demo是基于阻塞式api的,当程序输入或输出操作后,在操作返回前会一直阻塞线程。服务器需要为每一个客户端提供一个线程进行处理。当有大量的客户端的时候,性能比较底下。

JDK1.4 开始提供了 NIO(new io)开发高性能的服务器,可以使服务器使用一个或有限几个线程同时处理所有客户端

IO和NIO的区别

面向流和面向缓冲区

  • io面向流,从上面的demo可以看出所有输入输出都是通过流来完成。每次从流中读出数据它们没有被缓存在任何地方,直到被读完。需要需要前后移动数据需要先将数据缓存到一个缓冲区。

  • nio面向缓冲区,面向块。使用channel(通道)模拟传统输入输出流,所有从channel读取的数据或是发送的数据都需要先放到buffer(缓冲)中。程序不能直接对channel直接操作。使数据操作更加灵活

阻塞式和非阻塞式

  • 阻塞式如上面所述,需要阻塞直到数据完全读取写入。

  • 非阻塞式,一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞io的空闲时间用于在其它通道上执行io操作,所以一个单独的线程现在可以管理多个输入和输出通道。

Selector

nio通过Selector(选择器)来实现一个线程对多个channel的管理。

服务器上的所有channel都需要向Selector注册,而Selector负责监视这些channel的状态。当有任意个channel有可用的io操作时,Selectorselect()方法会返回一个大于0的值,表示当前有多少channel有可用的io操作。可以通过selectedKeys()获取channel集合。所以只需要一个线程不断调用select()方法即可。

Tips:无可用的channel时,调用select()方法的线程会被阻塞。

Netty

Netty 是业界流行的 NIO 框架之一,它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都说首屈一指的,也已经得到了成百上千商用项目的验证。

优点有很多..... 反正就是很好用 很耐用,而且开发门槛低。.

Netty Api