三不朽

Java 网络NIO学习笔记--多线程的阻塞式Java TCP服务示例

Publish: March 16, 2021 Category: 代码分享 No Comments

Java NIO中涉及的基础内容:

  • Channel 通道
  • Buffer 缓冲区
  • File IO 文件IO
  • Network IO 网络IO

最简单的基于Socket的服务端多线程模型,服务器为每一个客户端派发一个线程,新的线程只负责单独的客户端数据。为了接受客户端连接,服务端会有一个单独的派发线程。

package testnio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 多线程的Java TCP服务示例
 */
public class MultiThreadEchoServer {
    //可缓存线程池 用来处理每一个客户端
    private static ExecutorService tp = Executors.newCachedThreadPool();

    /**
     * 内部类,负责创建线程并处理socket数据
     */
    static class HandleMsg implements Runnable{

        //读取Socket内容并返回
        Socket serverSocket;
        public HandleMsg(Socket serverSocket){
            this.serverSocket = serverSocket;
        }

        @Override
        public void run() {
            BufferedReader inStream = null;
            PrintWriter outStream = null;

            try {
                inStream = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
                outStream = new PrintWriter(serverSocket.getOutputStream(), true);
                String inputLine = null;
                //处理客户端请求
                long b = System.currentTimeMillis();
                while ((inputLine = inStream.readLine()) != null){
                    outStream.println(inputLine);
                }
                long e = System.currentTimeMillis();
                System.out.println("spend:" + (e-b)+"ms");

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    if (inStream != null) inStream.close();
                    if (outStream != null) outStream.close();
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    /**
     * 入口方法,根据连接创建新的处理线程和服务端Socket来处理业务
     * @param args
     */
    public static void main(String[] args) {
        ServerSocket echoServer = null;
        Socket serverSocket = null;

        try {
            echoServer = new ServerSocket(8001);
        } catch (IOException e) {
            e.printStackTrace();
        }

        while (true){
            try {
                //accept()方法会一致阻塞在这里,直到新的客户端连接到来
                serverSocket = echoServer.accept();
                System.out.println(serverSocket.getRemoteSocketAddress() + " connect!");
                tp.execute(new HandleMsg(serverSocket));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


}

阅读剩余部分...

GStreamer Android 教程(零)如何运行官方Demo

Publish: March 11, 2021 Category: 小技巧 No Comments

本文主要记录如何将官方提供的Android Demo运行起来,以笔记形式记录,方便有需要的同学做个参考。

开发工具Android Studio,操作系统 Windows10

1 获取示例源码
在gst-docs仓库中 https://gitlab.freedesktop.org/gstreamer/gst-docs/ ,克隆该仓库到本地。

然后在Android Studio中打开这个项目目录(gst-docs/examples/tutorials/android),注意不是里面的android-tutorial-1 android-tutorial-2的子项目目录。

2 gradle.properties中添加
gstAndroidRoot 是指定GStreamer sdk路径,如果是按照官方教程添加了环境变量 GSTREAMER_ROOT_ANDROID 则不需要添加此项目。

gstAndroidRoot=E\:\\common_sdk\\gstreamer-1.0-android-universal-1.18.3

项目的ndkbuild配置项要求指定该目录



阅读剩余部分...

GStreamer Android 教程(一)在Android程序中使用GStreamer

Publish: March 4, 2021 Category: 编程 No Comments

0 目标演示

在APP中显示一个GStreamer版本。

这第一篇 Android 教程非常简单:它只是检索 GStreamer 版本并将其显示在屏幕上。演示如何从Java访问GStreamer C代码,并验证了没有任何链接问题。

1 Hello GStreamer (Java 代码)

教程代码在tutorials/android-tutorial-1子目录下的gst-docs中。这个目录包含通常的Android NDK结构:一个src文件夹用于Java代码,一个jni文件夹用于C代码,一个res文件夹用于UI资源。
我们建议你在Eclipse中打开这个项目(正如在安装Android开发中所解释的那样),这样你就可以很容易地看到所有的部分是如何结合在一起的。
让我们先介绍一下Java代码,然后是C代码,最后是允许GStreamer集成的makefile。

//src/org/freedesktop/gstreamer/tutorials/tutorial_1/Tutorial1.java
package org.freedesktop.gstreamer.tutorials.tutorial_1;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import org.freedesktop.gstreamer.GStreamer;

public class Tutorial1 extends Activity {
    private native String nativeGetGStreamerInfo();

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        try {
            GStreamer.init(this);
        } catch (Exception e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        setContentView(R.layout.main);

        TextView tv = (TextView)findViewById(R.id.textview_info);
        tv.setText("Welcome to " + nativeGetGStreamerInfo() + " !");
    }

    static {
        System.loadLibrary("gstreamer_android");
        System.loadLibrary("tutorial-1");
    }

}

上面的代码中演示了声明一个从Java中调用C本地方法,就是这个方法

private native String nativeGetGStreamerInfo();

表示,告诉Java程序该方法已经存在,编译器可以继续编译,并且保证在运行时可调用。具体的实现会在后面说明。
第一个被实际执行的代码是类的静态初始化类。

static {
    System.loadLibrary("gstreamer_android");
    System.loadLibrary("tutorial-1");
}

此处声明表示加载libgstreamer_android.so和libtutorial-1.so两个库,前者包含GStreamer的全部方法,后者是本示例中所用到的方法,后面会说明。
在加载时,这些库的JNI_OnLoad()方法都会被执行。它基本上注册了这些库所公开的本地方法。GStreamer库只暴露了一个init()方法,它初始化了GStreamer,并注册了所有的插件(教程库将在后面解释)。

try {
    GStreamer.init(this);
} catch (Exception e) {
    Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
    finish();
    return;
}

接下来,在Activity的OnCreate()方法中,我们实际上是通过调用GStreamer.init()来初始化GStreamer。这个方法需要一个Context,所以不能从静态初始化器中调用,但是多次调用它并没有危险,因为除了第一次之外,所有的调用都会被忽略。
如果初始化失败,init()方法将抛出一个由GStreamer库提供异常详情的Exception。

TextView tv = (TextView)findViewById(R.id.textview_info);
tv.setText("Welcome to " + nativeGetGStreamerInfo() + " !");

然后,调用本地方法nativeGetGStreamerInfo(),并获取一个字符串,用来格式化UI中TextView的内容。
本教程的UI部分到此结束。我们来看看C代码。







阅读剩余部分...

Win10迁移wsl子系统到其他磁盘或目录

Publish: February 20, 2021 Category: 小技巧 No Comments

使用这个开源工具 LxRunOffline https://github.com/DDoSolitary/LxRunOffline/releases

查看安装的WSL子系统
在Windows Terminal工具中执行 LxRunOffline.exe list

cd C:\Users\yinli\Downloads\LxRunOffline-v3.5.0-mingw
.\LxRunOffline.exe list

由于我的系统中只安装了ubuntu 显示结果如下:

PS C:\Users\yinli\Downloads\LxRunOffline-v3.5.0-mingw> .\LxRunOffline.exe list
Ubuntu-18.04

停止WSL服务
ctrl + shift + esc 找到LxssManager服务。找到并停止该服务。在任务管理器,服务选项卡中查找。

迁移
执行以下命令,经过漫长的等待之后可查看结果。

./LxRunOffline.exe move -n Ubuntu-18.04 -d E:/WSL/Ubuntu-18.04

查看结果

./LxRunOffline.exe get-dir -n Ubuntu-18.04

浏览器数据库 indexedDB 入门学习使用dexie.js封装

Publish: February 20, 2021 Category: WEB No Comments

基础知识可以在这里学习一下:

http://www.ruanyifeng.com/blog/2018/07/indexeddb.html 【浏览器数据库 IndexedDB 入门教程】

有一个对indexedDB的封装,https://github.com/dfahlander/Dexie.js 实现了丰富的操作。

1 快速入门:
1.1 数据库定义(declare database)

var db = new Dexie("MyDatabase");
db.version(1).stores({
    friends: "++id, name, age, *tags",
    gameSessions: "id, score"
});

提示: 不要在sql中声明所有的列,只需要声明需要建立索引的列,上面声明的几个列都是需要在where查询条件中要用到的列。

数据库定义语法

++   自增主键(Auto-incermented primary key)
&    唯一(Unique)
*    多条目索引(Multi-entry index)
[A+B]    复合索引(Compound Index)

提示:
① indexedDB中的多条目索引是指数组属性的索引,其中数组中的每个项都指向object/record/document。它类似于PostgreSQL中的GIN索引。
② compound(或composite)索引是基于多个keypath的索引。它可以用于在一个索引中高效地索引多个属性,以便轻松找到两个键及其值的组合的存在性。在dexe中,复合索引必须在定义数据库模式的时候分配。




阅读剩余部分...