2025年2月8日土曜日

Go言語入門(10)

Serverを32bit Windows実行ファイルにして、Clientを64bit Windows実行ファイルにして、アーキテクチャによらずに通信できることを確認する。さらに、Serverは32bit Windows DLLから戻り値を取得するようにしてみる。



ようやくだんだんと本題に近づいてきた気がする。では、Go言語入門(9)でやったのと同じようにすすめていく。
mkdir grpctry2
cd grpctry2
mkdir proto
mkdir server
mkdir client
go mod init grpc/grpctry2
そんでもって、protoを書く。こんな感じ。ま~たそれかい的なやつ。

proto/grpctry2.proto
  1. syntax = "proto3";
  2. package grpctry2;
  3. option go_package = "./proto";
  4. service MyAESEncrypter {
  5.   rpc AESEncrypt (Plain_and_Key) returns (Cipher) {}
  6. }
  7.  
  8. message Plain_and_Key {
  9.   bytes plain=1;
  10.   bytes key=2;
  11. }
  12.  
  13. message Cipher {
  14.   bytes result=1;
  15. }
そういえば、gRPCの環境構築の際にprotoc-gen-goとprotoc-gen-go-grpcのPATHをその場限りな表現で追加していた。なので、毎回PATHを確認して通ってなかったらPATHを通すってやらんといかん。~/.profileとか~/.bashrcとかに書いておけばいいんだけど、まぁその場限りの関係ってのでもいいんじゃないかと思う。
echo $PATH | grep go
ってすれば、PATHが通っていればPATH全体が表示されるし、通ってなければ何も表示されない。
PATHが通ってなければ、
PATH="$PATH:$HOME/go/bin"
ってする。
で、
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/grpctry2.proto
例によって、無言で2つの.pb.goファイルができる。
で、まずはClientを作る。これはGo言語入門(9)のClientのコードをそのまんま持ってきて、.pb.goを見ながら修正していくってかんじ。

client/main.go
  1. package main
  2.  
  3. import (
  4.     "context"
  5.     "flag"
  6.     "fmt"
  7.     "log"
  8.     "time"
  9.  
  10.     pb "grpc/grpctry2/proto"
  11.  
  12.     "google.golang.org/grpc"
  13.     "google.golang.org/grpc/credentials/insecure"
  14. )
  15.  
  16. var (
  17.     addr = flag.String("addr", "localhost:50051", "the address to connect to")
  18. )
  19.  
  20. func main() {
  21.     p := []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}
  22.     k := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
  23.     flag.Parse()
  24.     // Set up a connection to the server.
  25.     conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
  26.     if err != nil {
  27.         log.Fatalf("did not connect: %v", err)
  28.     }
  29.     defer conn.Close()
  30.     c := pb.NewMyAESEncrypterClient(conn)
  31.  
  32.     // Contact the server and print out its response.
  33.     ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  34.     defer cancel()
  35.     r, err := c.AESEncrypt(ctx, &pb.PlainAnd_Key{Plain: p, Key: k})
  36.     if err != nil {
  37.         log.Fatalf("could not greet: %v", err)
  38.     }
  39.     var res []byte = r.GetResult()
  40.     fmt.Printf("% 02X\n", res)
  41. }
で、実行ファイルを作る。
go get google.golang.org/grpc
cd client
GOOS=windows GOARCH=amd64 go build -o ../grpctry2_client.exe main.go
cd ..
(実行ファイルがプロジェクトルートに出力されるようにした。)
で、とりあえずServer側もGo言語入門(9)のServerのコードをそのまんま持ってきて.pb.goを見ながら修正していく。

server/main.go
  1. package main
  2. import (
  3.     "context"
  4.     "flag"
  5.     "fmt"
  6.     "log"
  7.     "net"
  8.  
  9.     pb "grpc/grpctry2/proto"
  10.  
  11.     "google.golang.org/grpc"
  12. )
  13.  
  14. var (
  15.     port = flag.Int("port", 50051, "The server port")
  16. )
  17.  
  18. // server is used to implement helloworld.GreeterServer.
  19. type server struct {
  20.     pb.UnimplementedMyAESEncrypterServer
  21. }
  22.  
  23. // SayHello implements helloworld.GreeterServer
  24. func (s *server) AESEncrypt(_ context.Context, in *pb.PlainAnd_Key) (*pb.Cipher, error) {
  25.     var d1 []byte = in.GetPlain()
  26.     var d2 []byte = in.GetKey()
  27.     d3 := []byte{}
  28.     for i := 0; i < 16; i++ {
  29.         d3 = append(d3, d1[i]^d2[i])
  30.     }
  31.  
  32.     return &pb.Cipher{Result: d3}, nil
  33. }
  34. func main() {
  35.     flag.Parse()
  36.     lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
  37.     if err != nil {
  38.         log.Fatalf("failed to listen: %v", err)
  39.     }
  40.     s := grpc.NewServer()
  41.     pb.RegisterMyAESEncrypterServer(s, &server{})
  42.     log.Printf("server listening at %v", lis.Addr())
  43.     if err := s.Serve(lis); err != nil {
  44.         log.Fatalf("failed to serve: %v", err)
  45.     }
  46. }
こんなかんじ。で、32bit Windows実行ファイルにする。
cd server
GOOS=windows GOARCH=386 go build -o ../grpctry2_server.exe main.go
cd ..
で、実行してみる。
こんな感じで、gPRCを通すことで64bit Windowsアプリから32bit Windowsアプリの機能を実行させることができる。
では、Go言語入門(5)を参考にrijndael.dllを使ってAES暗号化するってのを実装してみる。

server/main.go
  1. package main
  2.  
  3. import (
  4.     "context"
  5.     "flag"
  6.     "fmt"
  7.     "log"
  8.     "net"
  9.     "syscall"
  10.     "unsafe"
  11.  
  12.     pb "grpc/grpctry2/proto"
  13.  
  14.     "google.golang.org/grpc"
  15. )
  16.  
  17. var (
  18.     hdll, _ = syscall.LoadLibrary("rijndael.dll")
  19.     aes128encrypt, _ = syscall.GetProcAddress(hdll, "AES128Encrypt")
  20.     aes128decrypt, _ = syscall.GetProcAddress(hdll, "AES128Decrypt")
  21. )
  22.  
  23. func AES128Encrypt(plain *uint8, key *uint8, crypted *uint8) (r uint32) {
  24.     var nargs uintptr = 3
  25.     ret, _, _ := syscall.Syscall9(uintptr(aes128encrypt),
  26.         nargs,
  27.         uintptr(unsafe.Pointer(plain)),
  28.         uintptr(unsafe.Pointer(key)),
  29.         uintptr(unsafe.Pointer(crypted)),
  30.         0, 0, 0, 0, 0, 0)
  31.     r = uint32(ret)
  32.     return
  33. }
  34. func AES128Decrypt(crypted *uint8, key *uint8, plain *uint8) (r uint32) {
  35.     var nargs uintptr = 3
  36.     ret, _, _ := syscall.Syscall9(uintptr(aes128decrypt),
  37.         nargs,
  38.         uintptr(unsafe.Pointer(crypted)),
  39.         uintptr(unsafe.Pointer(key)),
  40.         uintptr(unsafe.Pointer(plain)),
  41.         0, 0, 0, 0, 0, 0)
  42.     r = uint32(ret)
  43.     return
  44. }
  45.  
  46. var (
  47.     port = flag.Int("port", 50051, "The server port")
  48. )
  49.  
  50. type server struct {
  51.     pb.UnimplementedMyAESEncrypterServer
  52. }
  53.  
  54. func (s *server) AESEncrypt(_ context.Context, in *pb.PlainAnd_Key) (*pb.Cipher, error) {
  55.     var d1 []byte = in.GetPlain()
  56.     var d2 []byte = in.GetKey()
  57.     var d1arr [16]byte = *(*[16]byte)(d1[:16])
  58.     var d2arr [16]byte = *(*[16]byte)(d2[:16])
  59.     var d3arr [16]byte
  60.     defer syscall.FreeLibrary(hdll)
  61.     AES128Encrypt(&d1arr[0], &d2arr[0], &d3arr[0])
  62.     return &pb.Cipher{Result: d3arr[:]}, nil
  63. }
  64. func main() {
  65.     flag.Parse()
  66.     lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
  67.     if err != nil {
  68.         log.Fatalf("failed to listen: %v", err)
  69.     }
  70.     s := grpc.NewServer()
  71.     pb.RegisterMyAESEncrypterServer(s, &server{})
  72.     log.Printf("server listening at %v", lis.Addr())
  73.     if err := s.Serve(lis); err != nil {
  74.         log.Fatalf("failed to serve: %v", err)
  75.     }
  76. }
でけた。sliceを配列に変換してdllをcallして、戻りの配列をsliceに変換するってワン(ツー?)クッションが必要になる。ちなみに、実行ファイルとDLLを同じフォルダに入れることを前提にしている(LoadLibraryのとこ)。で、serverをコンパイルしてっと。んで、実行してみる。
こんな感じで、64bit Windows実行ファイルから32bit DLLの実行結果を得ることができる。イイゾ!

フリーアドレスって本当は生産性低下を招いている。って思っている人はどのくらいいるんだろう。固定席なら自然にわかってたことが、あえてヒアリングしないとわからなくなってる。今さら座席を増やす土地やカネがないって目先のことにとらわれてるのならキズが深くなる前に見直してほしい。

1 件のコメント:

  1. ヒマナッツ2025年2月16日 17:21

    defer syscall.FreeLibrary(hdll)
    はAESEncrypt関数内ではダメ。これだと一度AESEncrypt関数が実行されて完了するとDLLが解放されてしまう。main内に入れるのがたぶん良い。

    返信削除