zoukankan      html  css  js  c++  java
  • Android Socket

    Android Socket

    参考资料

    菜鸟教程

    怎么理解TCP的面向连接和UDP的无连接

    https://www.cnblogs.com/xiaomayizoe/p/5258754.html

    https://www.cnblogs.com/qifengshi/p/6602881.html

    Overview

    Socket 一词起源于UNIX 操作系统,UNIX 系统中将IP + 端口号 的通信的方式称之为Socket 中文译为套接字 ,在学习Socket的时候不要太过于纠结它的名字,毕竟只是个代号。

    TCP和UDP

    首先要想比较容易的理解TCP和UDP协议,首先得对整体的网络协议框架有一个整体的了解,并不需要多清楚,只要把握整体就好了,这里我推荐 阮一峰 阮老师的博客 网络协议入门1 , 网络协议入门2

    这里的TCP 协议不等于TCP/IP协议族 ,这一点不要弄混。

    首先呢UDP 是先与TCP 出现的,但是UDP协议是存在着一些“问题”的,所有后来就有了更为复杂的TCP协议。

    UDP(User Datagram Protocol)协议

    如果已经知道对方的IP端口号 ,通过UDP协议我们是已经可以实现,不同的计算机的程序之间的通信了,但是UDP协议还存在着一个“问题”。

    • UDP协议是一种无连接的协议(发送数据之前无需建立连接),不能确保服务器是否收到了发送的数据,这就不能保证数据的完成性,当服务器高负载的情况下很容易出现。
    • UDP协议无法保证发送数据的顺序

    比如说,通过UDP协议向服务器发送了如下的数据

    11

    22

    33

    但是服务器可能接收到的是

    33

    11

    22

    但是这种,情况一般是在网络拥堵的情况下才会出现。

    TCP(Transmission Control Protocol) 协议

    因为UDP的一些“弊端”,所以TCP协议出现了,TCP协议,面向连接,发送数据有顺序,不会造成丢包,但是一些代价也随之而来。TCP与UDP相比消耗的资源较多。

    TCP 三次握手

    https://www.cnblogs.com/qifengshi/p/6602881.html

    总结

    当对数据的完整性非常高的的时候,需要采用TCP协议传输,比如传输文件 ,发送邮件

    当对数据完成行要求不高的时候,可以采用UDP协议,比如视频通话

    UDP 和 TCP 没有绝对的好与坏,之后谁更适用。

    InetAddress

    This class represents an Internet Protocol (IP) address.

    该类代表着一个IP地址.

    /**
     * 获取本机信息
     * 主机名
     * IP地址
     */
    private void getLocalHostInfo() {
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            String hostName = inetAddress.getHostName();
            String ipAddress = inetAddress.getHostAddress();
            System.out.printf("HostName:%s IP Address:%s", hostName, ipAddress);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
    

    基于TCP的Socket

    一个DEMO

    通过Socket实现文件上传到服务器的功能,上传功能具有断点续传机制。

    效果图如下

    实现思路如下

    首先,需要在Android 的 Assets 目录下,放入一个名为tmep.flv 的视频文件,

    客户端实现思路
    1. 建立与服务端的连接
    2. 选择要上传的文件
    3. 在写入文件的时候,要先想服务器发送头信息(如: 要上传的文件的名称)
    4. 当暂停的时候释放Socket占用的资源,并且记录已经写入了的长度
    5. 当再次开始的时候,获取已经写入了的长度,在写入的时候通过流的skip 方法,跳过这些流。
    6. 建立新的Socket连接,继续向服务器发送数据

    PS: 在写入的时候会更新进度条,来显示写入进度

    服务端实现思路
    1. 开启服务等待客户端了连接
    2. 当客户端连接后,首先获取客户端传递过来的头信息
    3. 获取到头信息的文件名后,在本地建立文件并写入
    4. 当客户端暂停后,清空资源,等待客户端再次连接
    5. 当客户端再次连接后,向本地已经存在的文件中追加数据
    注意事项
    • 在没有完全地交互完的时候,不要关闭流,如果关闭了流,那么Socket连接也会关闭,会抛出SocketClosed 异常。

    代码实现

    服务端代码

    /**
     * 通过Socket来实现上传操作
     */
    public class SocketUploadClass implements ICallAble {
    
        private ServerSocket serverSocket;
    
        //线程池
        ExecutorService executorService;
    
        @Override
        public void call() {
            try {
                startServer();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 初始化服务器
         */
        private void startServer() throws IOException {
            serverSocket = new ServerSocket(9988);
            //创建线程池 线程数 = 可用cpu 数量 * 50
            this.executorService = 
              Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50);
            //等待客户端接入
            while (true) {
                Socket socket = serverSocket.accept();
                executorService.execute(new SocketTask(socket));
            }
        }
    
        /**
         * 用来处理用户写入的县城类
         */
        class SocketTask implements Runnable {
            //连接到服务器的线程
            Socket socket;
    
            public SocketTask(Socket socket) {
                this.socket = socket;
            }
    
            @Override
            public void run() {
                String ipAddress = socket.getInetAddress().toString();
                int port = socket.getPort();
                System.out.printf("Client 【%s】 Port:%d 已连接", ipAddress, port);
    
                //接收数据
                try {
                    InputStream inputStream = socket.getInputStream();
                    BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                    //读取第一行的头信息
                    String header = "";
                    int i;
                    while ((i = inputStream.read()) != -1) {
                        if (i == '
    ')
                            break;
                        header += (char) i;
                    }
                    System.out.println(header);
    
                    //要存储的文件名
                    String fileName = header.split("=")[1];
                    //将传递过来的内容写入到本地文件
                    byte[] buffer = new byte[1024];
                    FileOutputStream fileOutputStream = new FileOutputStream(fileName, true);
                    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
                    int len;
                    while ((len = bufferedInputStream.read(buffer)) != -1) {
                        bufferedOutputStream.write(buffer, 0, len);
                    }
                    //释放资源
                    bufferedOutputStream.flush();
                    bufferedInputStream.close();
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    客户端代码

    布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">
    
        <Button
            android:id="@+id/uploadBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="upload"
            android:text="Update"/>
    
        <Button
            android:id="@+id/pauseBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="pause"
            android:text="Pause"/>
    
        <ProgressBar
            android:id="@+id/processPB"
            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    
    

    Activity代码

    public class SocketUploadActivity extends CommonActivity {
    
        private ProgressBar mProgressBar;
        private Button mPauseBtn;
        private Button mUploadBtn;
        private Socket mSocket;
    
        //要上传的文件名称
        private String fileName = "temp.flv";
    
        //标识是否开始写入
        private boolean start = true;
    
        private int mCurrentLen;
    
        @Override
        public int layoutId() {
            return R.layout.activity_socket_upload;
        }
    
        @Override
        public void init() {
            mUploadBtn = (Button) this.findViewById(R.id.uploadBtn);
            mPauseBtn = (Button) this.findViewById(R.id.pauseBtn);
            mProgressBar = (ProgressBar) this.findViewById(R.id.processPB);
        }
    
        /**
         * 上传文件
         */
        public void upload(View view) {
            AssetManager manager = this.getAssets();
    
            new Thread(() -> {
                try {
                    start = true;
    
                    if (mSocket == null || mSocket.isClosed())
                        mSocket = new Socket("10.0.2.2", 9988);
    
                    InputStream inputStream = manager.open(fileName);
                    //当前已经读取到的总长度
                    mCurrentLen = getSharedPreferences("socketConfig", MODE_PRIVATE).getInt(fileName, 0);
                    int totalLen = inputStream.available(); //总长度
                    int currentRate = 0;
                    byte[] buffer = new byte[1024];
                    int len;
                    //读取文件
                    OutputStream outputStream = mSocket.getOutputStream();
                    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
                    //跳过已经传输了的宽度
                    inputStream.skip(mCurrentLen);
                    //写入一行头信息,告知服务器一些关于文件的信息,这里仅仅写入了文件的名称,也可以写入其他的
                    bufferedOutputStream.write(("file=" + fileName + "
    ").getBytes("UTF-8"));
    
                    //当用户没有暂停上传,并且还有数据的时候,写入数据
                    while (start && (len = inputStream.read(buffer)) != -1) {
                        bufferedOutputStream.write(buffer, 0, len);
                        //计算已经写入了的长度
                        mCurrentLen += len;
    
                        //计算已经上传了的比例
                        int rate = (int) Math.floor(mCurrentLen * 1.0 / totalLen * 100);
    
                        //更新进度,更新进度条不需要返回UI线程,直接更新就行
                        if (rate > currentRate) {
                            currentRate = rate;
                            mProgressBar.setProgress(rate);
                            L.e("Total Len=: " + totalLen + " Rate:" + rate);
                        }
                    }
                    //释放资源
                    bufferedOutputStream.flush();
                    inputStream.close();
                    bufferedOutputStream.close();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    
        /**
         * 暂停写入,关闭Socket连接,将已经写入的长度进行存储,实现断点续传
         */
        public void pause(View view) throws IOException {
            start = false;
            //为了进行断点续传,将已经写入的数据的长度存储起来
            SharedPreferences.Editor editor = 
              this.getSharedPreferences("socketConfig", MODE_PRIVATE).edit();
            editor.putInt(fileName, mCurrentLen);
            editor.apply();
            mSocket.close();
        }
    }
    
    

    检验是否上传成功

    看一看我们长传过来的视频是否能够正常播放,如果能够就证明我们上传成功了。

  • 相关阅读:
    Opencv3 ——读取图像,显示图像
    QT5生成log日志
    QT5串口读取宇电温控器温度
    QSettings 配置信息写入本地文件
    Qt5应用程序封包
    python 创建虚拟环境
    Ubuntu更换国内源
    一个关于百度编辑器的小问题
    JavaScript等比例缩放图片(转载)
    asp.net io操作,修改文件夹的名称,报错:access to the path is denied
  • 原文地址:https://www.cnblogs.com/slyfox/p/8708644.html
Copyright © 2011-2022 走看看