zoukankan      html  css  js  c++  java
  • Android 客户端和 web服务器通信

    本篇简单介绍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

  • 相关阅读:
    『ORACLE』 DG切换主备库角色(11g)
    Java基础语法(三)---数组
    JDK安装与配置详细图文教程
    谁把20岁上下的你给洗脑了
    看看已堕落的自己
    关于Git
    自定义UIDatePikerView
    细节关注(持续更新。。。)
    如何生成圆形的图片
    高效使用你的Xcode
  • 原文地址:https://www.cnblogs.com/chenjy1225/p/9662560.html
Copyright © 2011-2022 走看看