2022年11月27日 星期日

用 dart 語言寫簡單的 SocketServer 聊天室

// server.dart
import 'dart:io';
import 'dart:convert';
class ChatRoom {
  int key = 0;
  static int sn = 0;
  static Map<int, Socket> sockets = { };
  void broadcast(List<int> data) {
    sockets.forEach((id, session) {
      if (key != id) session.add(utf8.encode('id$key:>${utf8.decode(data)}'));
    });
  }
  void relay(List<int> data) async {
    String text = utf8.decode(data).trim();
    if (text == 'quit') {
      print('relay: $text');
      final session = sockets.remove(key);
      await session?.close();
    }
    broadcast(data);
  }
  void shutdown( ) {
    sockets.remove(key);
    broadcast(utf8.encode('bye!\n'));
    print('id$key leave, remain ${sockets.length}');  
  }
  ChatRoom (Socket session) {
    key = ++sn;
    session .. add(utf8.encode('id${key} welcome!\n')) .. listen(
      relay,
      onDone: shutdown,
      onError: (_) => session.close()
    );
    sockets[key] = session;
    print('id$key join [total ${sockets.length}]');
  }
}
void main( ) {
  int port = 8080;
  print("Chat room at $port");
  ServerSocket.bind(InternetAddress.anyIPv4, port).then((ServerSocket server) {
    server.listen((socket) => ChatRoom(socket));    
  });
}
使用 dart VM 來跑: dart server.dart, 之後就可以用  telnet 127.0.0.1  8080 讓客戶端連進聊天室, 只是 linux 上的 telnet client 在中文編/解碼上, 雖然接收時解中文碼沒問題, 但傳送時編成的中文碼可能會有問題, 幸好用 dart 語言編寫相同功能的 telnet 終端機不是難事:
// client.dart
import 'dart:io';
import 'dart:convert';
void main(List<String>  args)  async {
      String ip = (args.length  >  0) ?  args[0]  :  'localhost';
      int port  = (args.length  >  1)  ?  int.parse(args[1])  :  8080;
      Socket  session = await  Socket.connect(ip,  port);
      session.listen (
           (dynamic data) => print((data is String) ? data  : utf8.decode(data).trim()),
           onDone: (  )  =>  exit(0),
           onError: (_)  =>  session.close()
      );
      stdin.listen (
         (List<int> data) {
              try {//  print(data);
                if  (utf8.decode(data).trim() == 'quit')  throw 'user quit';
                else  session.add(data);
             } catch(e) {
               print('stdin: $e, $data');
               session.close();
               exit(0);
            }
         }
     );
}
同樣用 dart VM 來跑: dart client.dart

2022年11月25日 星期五

使用 crypto 產生密鑰並自我簽證

參考討論文: https://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl
// sudo  apt-get   install   libcrypto
// g++   genrsa.c  -lcrypto  &&  ./a.out
// genrsa.c
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <stdio.h>
int main( ) {
    int nbits = 2048;
    RSA *rsaKey = RSA_new();
    BIGNUM *bignF4 = BN_new();
    BN_set_word(bignF4, 0x10001);// bignF4 = 65537
    if (RSA_generate_key_ex(rsaKey, nbits, bignF4, NULL)) {
        long days = 365;
        unsigned char* subj[3] = {
            (unsigned char *)"TW",
            (unsigned char *)"SELF",
            (unsigned char *)"0.0.0.0"
        };
        X509 *certify = X509_new();
        X509_set_version(certify, 2);// Ver 3
        ASN1_INTEGER_set(X509_get_serialNumber(certify), 1);
        X509_gmtime_adj(X509_get_notBefore(certify), 0);
        X509_gmtime_adj(X509_get_notAfter(certify), days * 86400);
        X509_NAME *subjName = X509_get_subject_name(certify);
        X509_NAME_add_entry_by_txt(subjName, "C" , MBSTRING_ASC, subj[0], -1, -1, 0);
        X509_NAME_add_entry_by_txt(subjName, "O" , MBSTRING_ASC, subj[1], -1, -1, 0);
        X509_NAME_add_entry_by_txt(subjName, "CN", MBSTRING_ASC, subj[2], -1, -1, 0);
        X509_set_issuer_name(certify, subjName);
        EVP_PKEY *keyPair = EVP_PKEY_new();
        EVP_PKEY_assign_RSA(keyPair, rsaKey);
        X509_set_pubkey(certify, keyPair);
        X509_sign(certify, keyPair, EVP_sha1());
        FILE *fout = fopen("private.pem", "wb");
        PEM_write_PrivateKey(fout, keyPair, NULL, NULL, 0, NULL, NULL);
        fclose(fout);
        EVP_PKEY_free(keyPair);
        fout = fopen("certify.pem", "wb");
        PEM_write_X509(fout,  certify);
        fclose(fout);
        X509_free(certify);
    }
    BN_free(bignF4);
    RSA_free(rsaKey);
    return 0;
}

2022年11月24日 星期四

關於 Flutter 3.3.x 黑畫面問題

在 Linux 上使用 Flutter 3.0.5 版本, 寫 linux 的處理畫面(gui)小程式, 一直以來都沒什問題, 更新到 3.3.6 版, 就是無法繪圖, 即使增開一個測試程式:  flutter create test && cd test && flutter run, 也看不到點擊畫面, 只好延用 3.0.5 版, 到今天(2022.11.25), Flutter 早已更新到 3.3.9 版, 下載的 stable 版本仍然不正常, 於是就上網路查, 有人說目前 3.3.x stable  版都有此問題, 切換到 channel master 就可以了, 好奇之下, 開啟終端機, 下個命令:

         flutter    channel    master

畫面真的恢復了, 試試把它切回 flutter  channel  stable, 還真的不正常, 結論就是目前下載的 flutter 3.3.9 版仍要再切到 flutter master channel, 接著讓它上網自動更新, 之後 linux 上處理畫面的程式就能正常運作.

2022年11月23日 星期三

20 幾行的 http 伺服器(node js)

// http.js
const http = require('http');
const fs   = require('fs');
const objHtml = {'Content-Type' : 'text/html;charset=utf-8'};
const uService = (req, reply) => {
    const filename = (req.url == '/') ? './index.html' : `.${req.url}`;
    if (! fs.existsSync(filename)) {// console.log(filename);
        reply.writeHead(404, objHtml);
        reply.write('<!DOCTYPE html><html><body>Not Found<body></html>');
        reply.end();
        return;
    }
    const mimeType = filename.endsWith('.html') ? objHtml :
          filename.endsWith('.css') ? {'Content-Type' : 'text/css'} :
          filename.endsWith('.js')  ? {'Content-Type' : 'application/javascript'} :
          {'Content-Type' : 'application/octet-stream'};
    reply.writeHead(200, mimeType);
    fs.createReadStream(filename).pipe(reply);
}
const port = 8080;
console.log(`http://0.0.0.0:${port}/`);
http.createServer(uService).listen(port);
使用 nodejs 來跑:  node  http.js, 並用瀏覽器瀏覽網址  http://127.0.0.1:8080/index.html
改用 dart 語言來寫:
// http.dart
import 'dart:io';
final objHtml = ContentType('text', 'html', charset: 'UTF-8');
final uService = (HttpRequest req) {
    final reply = req.response;
    final filename = (req.uri.path == '/') ? './index.html' : '.${req.uri.path}';
    final file = File(filename);
    if (! file.existsSync()) {
        reply .. statusCode = 404 ..
            headers.contentType = objHtml ..
            write('<!DOCTYPE html><html><body>Not Found<body></html>') ..
            close();
        return;
    }
    reply .. statusCode = 200 ..
        headers.contentType = RegExp(r'(\.htm|\.html)$').hasMatch(filename) ? objHtml :
            RegExp(r'\.css$').hasMatch(filename) ? ContentType('text', 'css') :
            RegExp(r'\.js$').hasMatch(filename) ? ContentType('application', 'javascript') :
            ContentType('application', 'octet-stream') ..
        headers.contentLength = file.lengthSync();
    file.openRead().pipe(reply);
};
main() {
    final port = 8080;
    print("http://0.0.0.0:${port}/");
    HttpServer.bind(InternetAddress.anyIPv4, port).then((http)=>http.listen(uService));
}
使用 dart 來跑:  dart  http.dart, 並用瀏覽器瀏覽網址  http://127.0.0.1:8080/index.html

2022年11月5日 星期六

玩 Arch Linux

1. 官網下載 Iso 檔 https://archlinux.org/download/, 目前 2022.11.01 版支援 Kernel 6.06, youtube 有許多影片教導重頭至尾的安裝步驟
2. 我用 pacstrap下載以下套件,就足以實現一套完整的中文 linux x11 桌面系統在 VirtualBox 上跑:
base   linux   linux-firmware   base-devel   linux-headers    grub   dosfstools  squashfs-tools   xorriso   efibootmgr   os-prober   gdisk   parted   dhcpcd   networkmanager   network-manager-applet    dialog   wireless_tools   wpa_supplicant    openssh   git   virtualbox-guest-utils   xorg   lightdm   lightdm-gtk-greeter   mate   mate-extra     xorg-server     wqy-bitmapfont   wqy-zenhei   wqy-microhei   wqy-microhei-lite   opendesktop-fonts   ttf-arphic-ukai   ttf-arphic-uming    pulseaudio   pulseaudio-alsa   pavucontrol   materia-gtk-theme   papirus-icon-theme    firefox    xed    geany    gcin   vim

後記:
1. 下載並安裝完後再將  /var/cache/pacman/pkg 刪除可以能節省 1G 左右空間
2. 假設 EFI filesystem 在 /dev/sda1, 安裝 grub bootloader 到 /dev/sda2:
   mkdir   /mnt/efi   &&   mkdir   /mnt/boot
   mount   /dev/sda1  /mnt/efi   &&   mount   /dev/sda2    /mnt/boot
   grub-install   --target=x86_64-efi    --efi-directory=/mnt/efi    --boot-directory=/mnt/boot --bootloader-id=grub    /dev/sda

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...