From c63580ee3813326c4179d349897aca99a9895b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Mon, 22 Jul 2024 18:02:09 +0800 Subject: [PATCH] feat(c/libuv/demo): Add libuv demo echo_server refactor(c/libuv): Adjust comments and file names to accommodate merge --- _demo/libuv/echo_server.go | 109 +++++++++++++++++++++++ c/libuv/_demo/echo_server/echo_server.go | 109 +++++++++++++++++++++++ c/libuv/error.go | 87 ------------------ c/libuv/libuv.go | 39 +------- c/libuv/net.go | 22 ++++- c/libuv/timer.go | 14 ++- 6 files changed, 252 insertions(+), 128 deletions(-) create mode 100644 _demo/libuv/echo_server.go create mode 100644 c/libuv/_demo/echo_server/echo_server.go diff --git a/_demo/libuv/echo_server.go b/_demo/libuv/echo_server.go new file mode 100644 index 00000000..81d18148 --- /dev/null +++ b/_demo/libuv/echo_server.go @@ -0,0 +1,109 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + "unsafe" +) + +var DEFAULT_PORT c.Int = 8080 +var DEFAULT_BACKLOG c.Int = 128 + +var loop *libuv.Loop + +type WriteReq struct { + req libuv.Write + buf libuv.Buf +} + +func FreeWriteReq(req *libuv.Write) { + wr := (*WriteReq)(c.Pointer(req)) + c.Free(c.Pointer(wr.buf.Base)) + c.Free(c.Pointer(wr)) +} + +func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func EchoWrite(req *libuv.Write, status c.Int) { + if status != 0 { + c.Fprintf(c.Stderr, c.Str("write error: %s\n"), libuv.Strerror(status)) + } + defer FreeWriteReq(req) +} + +func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + if nread > 0 { + req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) + if req == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) + defer c.Free(c.Pointer(buf.Base)) + return + } + req.buf = libuv.InitBuf(buf.Base, c.Uint(nread)) + (*libuv.Write)(c.Pointer(req)).Write(client, []libuv.Buf{req.buf}, 1, EchoWrite) + return + } + if nread < 0 { + if (libuv.Errno)(nread) != libuv.EOF { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) + } + (*libuv.Handle)(client).Close(nil) + } + if buf.Base != nil { + c.Free(c.Pointer(buf.Base)) + } +} + +func OnNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) + return + } + + client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) + + if client == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) + return + } + + if libuv.InitTcp(loop, client) < 0 { + c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) + c.Free(c.Pointer(client)) + return + } + + if server.Accept((*libuv.Stream)(client)) == 0 { + (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) + } else { + (*libuv.Handle)(client).Close(nil) + } +} + +func main() { + // Initialize the default event loop + loop = libuv.DefaultLoop() + + // Initialize a TCP server + var server libuv.Tcp + libuv.InitTcp(loop, &server) + + // Set up the address to bind the server to + var addr net.SockaddrIn + libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) + + // Bind the server to the specified address and port + (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) + res := (*libuv.Stream)(&server).Listen(DEFAULT_BACKLOG, OnNewConnection) + if res != 0 { + c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(res)) + return + } + + // Start listening for incoming connections + loop.Run(libuv.RUN_DEFAULT) +} diff --git a/c/libuv/_demo/echo_server/echo_server.go b/c/libuv/_demo/echo_server/echo_server.go new file mode 100644 index 00000000..81d18148 --- /dev/null +++ b/c/libuv/_demo/echo_server/echo_server.go @@ -0,0 +1,109 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + "unsafe" +) + +var DEFAULT_PORT c.Int = 8080 +var DEFAULT_BACKLOG c.Int = 128 + +var loop *libuv.Loop + +type WriteReq struct { + req libuv.Write + buf libuv.Buf +} + +func FreeWriteReq(req *libuv.Write) { + wr := (*WriteReq)(c.Pointer(req)) + c.Free(c.Pointer(wr.buf.Base)) + c.Free(c.Pointer(wr)) +} + +func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func EchoWrite(req *libuv.Write, status c.Int) { + if status != 0 { + c.Fprintf(c.Stderr, c.Str("write error: %s\n"), libuv.Strerror(status)) + } + defer FreeWriteReq(req) +} + +func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + if nread > 0 { + req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) + if req == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) + defer c.Free(c.Pointer(buf.Base)) + return + } + req.buf = libuv.InitBuf(buf.Base, c.Uint(nread)) + (*libuv.Write)(c.Pointer(req)).Write(client, []libuv.Buf{req.buf}, 1, EchoWrite) + return + } + if nread < 0 { + if (libuv.Errno)(nread) != libuv.EOF { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) + } + (*libuv.Handle)(client).Close(nil) + } + if buf.Base != nil { + c.Free(c.Pointer(buf.Base)) + } +} + +func OnNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) + return + } + + client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) + + if client == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) + return + } + + if libuv.InitTcp(loop, client) < 0 { + c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) + c.Free(c.Pointer(client)) + return + } + + if server.Accept((*libuv.Stream)(client)) == 0 { + (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) + } else { + (*libuv.Handle)(client).Close(nil) + } +} + +func main() { + // Initialize the default event loop + loop = libuv.DefaultLoop() + + // Initialize a TCP server + var server libuv.Tcp + libuv.InitTcp(loop, &server) + + // Set up the address to bind the server to + var addr net.SockaddrIn + libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) + + // Bind the server to the specified address and port + (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) + res := (*libuv.Stream)(&server).Listen(DEFAULT_BACKLOG, OnNewConnection) + if res != 0 { + c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(res)) + return + } + + // Start listening for incoming connections + loop.Run(libuv.RUN_DEFAULT) +} diff --git a/c/libuv/error.go b/c/libuv/error.go index 95ee68dd..b9397cf9 100644 --- a/c/libuv/error.go +++ b/c/libuv/error.go @@ -93,93 +93,6 @@ const ( ERRNO_MAX ) -//var errnoDescriptions = map[Errno]string{ -// E2BIG: "argument list too long", -// EACCES: "permission denied", -// EADDRINUSE: "address already in use", -// EADDRNOTAVAIL: "address not available", -// EAFNOSUPPORT: "address family not supported", -// EAGAIN: "resource temporarily unavailable", -// EAI_ADDRFAMILY: "address family not supported", -// EAI_AGAIN: "temporary failure", -// EAI_BADFLAGS: "bad ai_flags value", -// EAI_BADHINTS: "invalid value for hints", -// EAI_CANCELED: "request canceled", -// EAI_FAIL: "permanent failure", -// EAI_FAMILY: "ai_family not supported", -// EAI_MEMORY: "out of memory", -// EAI_NODATA: "no address", -// EAI_NONAME: "unknown node or service", -// EAI_OVERFLOW: "argument buffer overflow", -// EAI_PROTOCOL: "resolved protocol is unknown", -// EAI_SERVICE: "service not available for socket type", -// EAI_SOCKTYPE: "socket type not supported", -// EALREADY: "connection already in progress", -// EBADF: "bad file descriptor", -// EBUSY: "resource busy or locked", -// ECANCELED: "operation canceled", -// ECHARSET: "invalid Unicode character", -// ECONNABORTED: "software caused connection abort", -// ECONNREFUSED: "connection refused", -// ECONNRESET: "connection reset by peer", -// EDESTADDRREQ: "destination address required", -// EEXIST: "file already exists", -// EFAULT: "bad address in system call argument", -// EFBIG: "file too large", -// EHOSTUNREACH: "host is unreachable", -// EINTR: "interrupted system call", -// EINVAL: "invalid argument", -// EIO: "i/o error", -// EISCONN: "socket is already connected", -// EISDIR: "illegal operation on a directory", -// ELOOP: "too many symbolic links encountered", -// EMFILE: "too many open files", -// EMSGSIZE: "message too long", -// ENAMETOOLONG: "name too long", -// ENETDOWN: "network is down", -// ENETUNREACH: "network is unreachable", -// ENFILE: "file table overflow", -// ENOBUFS: "no buffer space available", -// ENODEV: "no such device", -// ENOENT: "no such file or directory", -// ENOMEM: "not enough memory", -// ENONET: "machine is not on the network", -// ENOPROTOOPT: "protocol not available", -// ENOSPC: "no space left on device", -// ENOSYS: "function not implemented", -// ENOTCONN: "socket is not connected", -// ENOTDIR: "not a directory", -// ENOTEMPTY: "directory not empty", -// ENOTSOCK: "socket operation on non-socket", -// ENOTSUP: "operation not supported on socket", -// EOVERFLOW: "value too large for defined data type", -// EPERM: "operation not permitted", -// EPIPE: "broken pipe", -// EPROTO: "protocol error", -// EPROTONOSUPPORT: "protocol not supported", -// EPROTOTYPE: "protocol wrong type for socket", -// ERANGE: "result too large", -// EROFS: "read-only file system", -// ESHUTDOWN: "cannot send after transport endpoint shutdown", -// ESPIPE: "invalid seek", -// ESRCH: "no such process", -// ETIMEDOUT: "connection timed out", -// ETXTBSY: "text file is busy", -// EXDEV: "cross-device link not permitted", -// UNKNOWN: "unknown error", -// EOF: "end of file", -// ENXIO: "no such device or address", -// EMLINK: "too many links", -// EHOSTDOWN: "host is down", -// EREMOTEIO: "remote I/O error", -// ENOTTY: "inappropriate ioctl for device", -// EFTYPE: "inappropriate file type or format", -// EILSEQ: "illegal byte sequence", -// ESOCKTNOSUPPORT: "socket type not supported", -// ENODATA: "no data available", -// EUNATCH: "protocol driver not attached", -//} - type Errno c.Int //go:linkname TranslateSysError C.uv_translate_sys_error diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 004b223d..fb0c7664 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -17,12 +17,6 @@ const ( METRICS_IDLE_TIME ) -const ( - RUN_DEFAULT RunMode = iota - RUN_ONCE - RUN_NOWAIT -) - const ( UV_LEAVE_GROUP Membership = iota UV_JOIN_GROUP @@ -68,8 +62,6 @@ const ( type LoopOption c.Int -type RunMode c.Int - type Membership c.Int type HandleType c.Int @@ -85,9 +77,6 @@ type OsFd c.Int // ---------------------------------------------- /* Handle types. */ -type Loop struct { - Unused [0]byte -} type Handle struct { Unused [0]byte @@ -101,14 +90,6 @@ type Stream struct { Unused [0]byte } -type Tcp struct { - Unused [0]byte -} - -type Udp struct { - Unused [0]byte -} - type Pipe struct { Unused [0]byte } @@ -121,10 +102,6 @@ type Poll struct { Unused [0]byte } -type Timer struct { - Unused [0]byte -} - type Prepare struct { Unused [0]byte } @@ -183,17 +160,6 @@ type Connect struct { Unused [0]byte } -type UdpSend struct { - Unused [0]byte -} - -// ---------------------------------------------- - -type Buf struct { - Base *c.Char - Len uintptr -} - // ---------------------------------------------- /* Function type */ @@ -257,7 +223,7 @@ func (shutdown *Shutdown) Shutdown(stream *Stream, shutdownCb ShutdownCb) c.Int // ---------------------------------------------- -/* HandleT related function and method */ +/* Handle related function and method */ // llgo:link (*Handle).Ref C.uv_ref func (handle *Handle) Ref() {} @@ -317,9 +283,6 @@ func (handle *Handle) Fileno(fd *OsFd) c.Int { return 0 } -//go:linkname InitBuf C.uv_buf_init -func InitBuf(base *c.Char, len c.Uint) Buf - //go:linkname UvPipe C.uv_pipe func UvPipe(fds [2]Uv_File, readFlags c.Int, writeFlags c.Int) c.Int { return 0 diff --git a/c/libuv/net.go b/c/libuv/net.go index 1e5f7590..e2f824f6 100644 --- a/c/libuv/net.go +++ b/c/libuv/net.go @@ -62,6 +62,24 @@ type UdpFlags c.Int // ---------------------------------------------- +/* Handle types. */ + +type Tcp struct { + Unused [0]byte +} + +type Udp struct { + Unused [0]byte +} + +/* Request types. */ + +type UdpSend struct { + Unused [0]byte +} + +// ---------------------------------------------- + /* Function type */ // llgo:type C @@ -78,7 +96,7 @@ type UdpRecvCb func(handle *Udp, nread c.Long, buf *Buf, addr *net.SockAddr, fla // ---------------------------------------------- -/* TcpT related function and method */ +/* Tcp related function and method */ //go:linkname InitTcp C.uv_tcp_init func InitTcp(loop *Loop, tcp *Tcp) c.Int @@ -131,7 +149,7 @@ func TcpConnect(req *Connect, tcp *Tcp, addr *net.SockAddr, connectCb ConnectCb) // ---------------------------------------------- -/* UdpT related function and method */ +/* Udp related function and method */ //go:linkname InitUdp C.uv_udp_init func InitUdp(loop *Loop, udp *Udp) c.Int diff --git a/c/libuv/timer.go b/c/libuv/timer.go index 615da48b..fff1af5d 100644 --- a/c/libuv/timer.go +++ b/c/libuv/timer.go @@ -5,10 +5,22 @@ import ( _ "unsafe" ) +// ---------------------------------------------- + +/* Handle types. */ + +type Timer struct { + Unused [0]byte +} + +// ---------------------------------------------- + // llgo:type C type TimerCb func(timer *Timer) -/* TimerT related function and method */ +// ---------------------------------------------- + +/* Timer related function and method */ //go:linkname InitTimer C.uv_timer_init func InitTimer(loop *Loop, timer *Timer) c.Int