diff --git a/Makefile b/Makefile index 7fda9cd6f..9ea95a9bf 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ endif default: all all: libhv examples -examples: test timer loop tcp udp nc nmap httpd curl consul_cli +examples: test timer loop stream_server dgram_server nc nmap httpd curl consul_cli clean: $(MAKEF) clean SRCDIRS="$(ALL_SRCDIRS)" @@ -62,11 +62,11 @@ timer: prepare loop: prepare $(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/hloop_test.c" -tcp: prepare - $(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/tcp.c" +stream_server: prepare + $(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/stream_server.c" -udp: prepare - $(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/udp.c" +dgram_server: prepare + $(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/dgram_server.c" nc: prepare $(MAKEF) TARGET=$@ SRCDIRS=". base event" SRCS="examples/nc.c" diff --git a/Makefile.in b/Makefile.in index 29bc4657f..9b1f63aea 100644 --- a/Makefile.in +++ b/Makefile.in @@ -124,6 +124,10 @@ else CPPFLAGS += -DNDEBUG endif +ifeq ($(ENABLE_UDS), yes) + CPPFLAGS += -DENABLE_UDS +endif + CPPFLAGS += $(addprefix -D, $(DEFINES)) CPPFLAGS += $(addprefix -I, $(INCDIRS)) CPPFLAGS += $(addprefix -I, $(SRCDIRS)) diff --git a/README.md b/README.md index 5785bdb8d..15798804b 100644 --- a/README.md +++ b/README.md @@ -154,12 +154,19 @@ int main(int argc, char** argv) { } ``` ```shell -make tcp udp nc -bin/tcp 1111 +make stream_server dgram_server nc +# TCP server/client +bin/stream_server 1111 bin/nc 127.0.0.1 1111 - -bin/udp 2222 +# UDP server/client +bin/dgram_server 2222 bin/nc -u 127.0.0.1 2222 +# Unix stream socket server/client +bin/stream_server --unix ~/server.sock +bin/nc -U ~/client.sock ~/server.sock +# Unix datagram socket server/client +bin/dgram_server --unix ~/server.sock +bin/nc -D ~/client.sock ~/server.sock ``` ## BUILD @@ -173,8 +180,8 @@ bin/nc -u 127.0.0.1 2222 - make test # master-workers model - make timer # timer add/del/reset - make loop # event-loop(include idle, timer, io) - - make tcp # tcp server - - make udp # udp server + - make stream_server # stream socket server (TCP, Unix domain socket) + - make dgram_server # datagram socket server (UDP, Unix domain socket) - make nc # network client - make nmap # host discovery - make httpd # http server diff --git a/base/hplatform.h b/base/hplatform.h index 180b20f3b..4e4f63408 100644 --- a/base/hplatform.h +++ b/base/hplatform.h @@ -127,6 +127,13 @@ #include // for mkdir,rmdir,chdir,getcwd #include // for open,close,read,write,lseek,tell + #ifdef ENABLE_UDS + // TODO: support Unix domain socket on Windows + // #define HAVE_UDS + // #include + #warning "Unix domain socket is not yet supported on Windows" + #endif + #define MKDIR(dir) mkdir(dir) #ifndef S_ISREG #define S_ISREG(st_mode) (((st_mode) & S_IFMT) == S_IFREG) @@ -147,6 +154,11 @@ #include #include // for gethostbyname + #ifdef ENABLE_UDS + #define HAVE_UDS + #include // For Unix domain sockets + #endif + #define MKDIR(dir) mkdir(dir, 0777) #endif diff --git a/base/hsocket.c b/base/hsocket.c index ca74cec36..3b7932020 100644 --- a/base/hsocket.c +++ b/base/hsocket.c @@ -1,5 +1,81 @@ #include "hsocket.h" +static inline int BindImpl(sockaddr_u* localaddr, int type) { + // socket -> setsockopt -> bind + int sockfd = socket(localaddr->sa.sa_family, type, 0); + if (sockfd < 0) { + perror("socket"); + return -socket_errno(); + } + + // NOTE: SO_REUSEADDR means that you can reuse sockaddr of TIME_WAIT status + int reuseaddr = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(int)) < 0) { + perror("setsockopt"); + goto error; + } + + if (bind(sockfd, &localaddr->sa, sockaddrlen(localaddr)) < 0) { + perror("bind"); + goto error; + } + + return sockfd; +error: + closesocket(sockfd); + return socket_errno() > 0 ? -socket_errno() : -1; +} + +static inline int ConnectImpl(sockaddr_u* peeraddr, int nonblock) { + int connfd = socket(peeraddr->sa.sa_family, SOCK_STREAM, 0); + if (connfd < 0) { + perror("socket"); + return -socket_errno(); + } + if (nonblock) { + nonblocking(connfd); + } + int ret = connect(connfd, &peeraddr->sa, sockaddrlen(peeraddr)); +#ifdef OS_WIN + if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) { +#else + if (ret < 0 && socket_errno() != EINPROGRESS) { +#endif + perror("connect"); + goto error; + } + return connfd; +error: + closesocket(connfd); + return socket_errno() > 0 ? -socket_errno() : -1; +} + +static inline int ConnectTimeoutImpl(int connfd, int ms) { + int err; + socklen_t optlen = sizeof(err); + struct timeval tv = {ms/1000, (ms%1000)*1000}; + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(connfd, &writefds); + int ret = select(connfd+1, 0, &writefds, 0, &tv); + if (ret < 0) { + perror("select"); + goto error; + } + if (ret == 0) { + errno = ETIMEDOUT; + goto error; + } + if (getsockopt(connfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) < 0 || err != 0) { + goto error; + } + blocking(connfd); + return connfd; +error: + closesocket(connfd); + return socket_errno() > 0 ? -socket_errno() : -1; +} + const char* socket_strerror(int err) { #ifdef OS_WIN static char buffer[128]; @@ -68,30 +144,7 @@ int Bind(int port, const char* host, int type) { printf("unknown host: %s\n", host); return ret; } - - // socket -> setsockopt -> bind - int sockfd = socket(localaddr.sa.sa_family, type, 0); - if (sockfd < 0) { - perror("socket"); - return -socket_errno(); - } - - // NOTE: SO_REUSEADDR means that you can reuse sockaddr of TIME_WAIT status - int reuseaddr = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(int)) < 0) { - perror("setsockopt"); - goto error; - } - - if (bind(sockfd, &localaddr.sa, sockaddrlen(&localaddr)) < 0) { - perror("bind"); - goto error; - } - - return sockfd; -error: - closesocket(sockfd); - return socket_errno() > 0 ? -socket_errno() : -1; + return BindImpl(&localaddr, type); } int Listen(int port, const char* host) { @@ -117,27 +170,7 @@ int Connect(const char* host, int port, int nonblock) { //printf("unknown host: %s\n", host); return ret; } - int connfd = socket(peeraddr.sa.sa_family, SOCK_STREAM, 0); - if (connfd < 0) { - perror("socket"); - return -socket_errno(); - } - if (nonblock) { - nonblocking(connfd); - } - ret = connect(connfd, &peeraddr.sa, sockaddrlen(&peeraddr)); -#ifdef OS_WIN - if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) { -#else - if (ret < 0 && socket_errno() != EINPROGRESS) { -#endif - perror("connect"); - goto error; - } - return connfd; -error: - closesocket(connfd); - return socket_errno() > 0 ? -socket_errno() : -1; + return ConnectImpl(&peeraddr, nonblock); } int ConnectNonblock(const char* host, int port) { @@ -147,31 +180,60 @@ int ConnectNonblock(const char* host, int port) { int ConnectTimeout(const char* host, int port, int ms) { int connfd = Connect(host, port, 1); if (connfd < 0) return connfd; - int err; - socklen_t optlen = sizeof(err); - struct timeval tv = {ms/1000, (ms%1000)*1000}; - fd_set writefds; - FD_ZERO(&writefds); - FD_SET(connfd, &writefds); - int ret = select(connfd+1, 0, &writefds, 0, &tv); - if (ret < 0) { - perror("select"); - goto error; - } - if (ret == 0) { - errno = ETIMEDOUT; - goto error; + return ConnectTimeoutImpl(connfd, ms); +} + +#ifdef HAVE_UDS + +int BindUnix(const char* path, int type) { + sockaddr_u localaddr; + socklen_t addrlen = sizeof(localaddr); + memset(&localaddr, 0, addrlen); + int ret = sockaddr_unix_assign(&localaddr, path); + if (ret != 0) { + printf("socket path too long: %s", path); + return ret; } - if (getsockopt(connfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) < 0 || err != 0) { + return BindImpl(&localaddr, type); +} + +int ListenUnix(const char* path) { + int sockfd = BindUnix(path, SOCK_STREAM); + if (sockfd < 0) return sockfd; + if (listen(sockfd, SOMAXCONN) < 0) { + perror("listen"); goto error; } - blocking(connfd); - return connfd; + return sockfd; error: - closesocket(connfd); + closesocket(sockfd); return socket_errno() > 0 ? -socket_errno() : -1; } +int ConnectUnix(const char* path, int nonblock) { + sockaddr_u peeraddr; + socklen_t addrlen = sizeof(peeraddr); + memset(&peeraddr, 0, addrlen); + int ret = sockaddr_unix_assign(&peeraddr, path); + if (ret != 0) { + printf("socket path too long: %s", path); + return ret; + } + return ConnectImpl(&peeraddr, nonblock); +} + +int ConnectUnixNonblock(const char* path) { + return ConnectUnix(path, 1); +} + +int ConnectUnixTimeout(const char* path, int ms) { + int connfd = ConnectUnix(path, 1); + if (connfd < 0) return connfd; + return ConnectTimeoutImpl(connfd, ms); +} + +#endif + int Socketpair(int family, int type, int protocol, int sv[2]) { #ifdef OS_UNIX if (family == AF_UNIX) { diff --git a/base/hsocket.h b/base/hsocket.h index 2cbb24e20..9cedd95d2 100644 --- a/base/hsocket.h +++ b/base/hsocket.h @@ -51,6 +51,9 @@ typedef union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; +#ifdef HAVE_UDS + struct sockaddr_un sun; +#endif } sockaddr_u; // @param host: domain or ip @@ -64,6 +67,11 @@ static inline socklen_t sockaddrlen(sockaddr_u* addr) { else if (addr->sa.sa_family == AF_INET6) { return sizeof(struct sockaddr_in6); } +#ifdef HAVE_UDS + else if (addr->sa.sa_family == AF_UNIX) { + return sizeof(struct sockaddr_un); + } +#endif return sizeof(sockaddr_u); } @@ -99,7 +107,12 @@ static inline void sockaddr_set_port(sockaddr_u* addr, int port) { //#define INET_ADDRSTRLEN 16 //#define INET6_ADDRSTRLEN 46 +#ifdef HAVE_UDS +#define SOCKADDR_STRLEN sizeof(((struct sockaddr_un*)(NULL))->sun_path) +#else #define SOCKADDR_STRLEN 64 // ipv4:port | [ipv6]:port +#endif + #define SOCKADDR_STR(addr, buf) sockaddr_str((sockaddr_u*)addr, buf, sizeof(buf)) // NOTE: typeof(addr)=[sockaddr*, sockaddr_in*, sockaddr_in6*, sockaddr_u*] // char buf[SOCKADDR_STRLEN] = {0}; @@ -118,6 +131,11 @@ static inline const char* sockaddr_str(sockaddr_u* addr, char* buf, int len) { port = htons(addr->sin6.sin6_port); snprintf(buf, len, "[%s]:%d", ip, port); } +#ifdef HAVE_UDS + else if (addr->sa.sa_family == AF_UNIX) { + snprintf(buf, len, "%s", addr->sun.sun_path); + } +#endif return buf; } @@ -140,6 +158,17 @@ static inline int sockaddr_assign(sockaddr_u* addr, const char* host, int port) return 0; } +#ifdef HAVE_UDS +static inline int sockaddr_unix_assign(sockaddr_u* addr, const char* path) { + if (strnlen(path, SOCKADDR_STRLEN) >= SOCKADDR_STRLEN) { + return -1; + } + addr->sun.sun_family = AF_UNIX; + strcpy(addr->sun.sun_path, path); + return 0; +} +#endif + // socket -> setsockopt -> bind // @param type: SOCK_STREAM(tcp) SOCK_DGRAM(udp) // @return sockfd @@ -158,6 +187,14 @@ int ConnectNonblock(const char* host, int port); #define DEFAULT_CONNECT_TIMEOUT 5000 // ms int ConnectTimeout(const char* host, int port, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT)); +#ifdef HAVE_UDS +int BindUnix(const char* path, int type DEFAULT(SOCK_STREAM)); +int ListenUnix(const char* path); +int ConnectUnix(const char* path, int nonblock DEFAULT(0)); +int ConnectUnixNonblock(const char* path); +int ConnectUnixTimeout(const char* path, int ms DEFAULT(DEFAULT_CONNECT_TIMEOUT)); +#endif + // Just implement Socketpair(AF_INET, SOCK_STREAM, 0, sv); int Socketpair(int family, int type, int protocol, int sv[2]); diff --git a/config.mk b/config.mk index bc7761d93..e59125002 100644 --- a/config.mk +++ b/config.mk @@ -12,6 +12,8 @@ WITH_CONSUL=no # features # base/hsocket.c: replace gethostbyname with getaddrinfo ENABLE_IPV6=no +# event/hloop.h: Enable Unix domains socket server/client APIs +ENABLE_UDS=no # base/RAII.cpp: Windows MiniDumpWriteDump ENABLE_WINDUMP=no # http/http_content.h: QueryParams,MultiPart diff --git a/event/hloop.c b/event/hloop.c index 8ae3f19a3..8c51c6c96 100644 --- a/event/hloop.c +++ b/event/hloop.c @@ -235,6 +235,33 @@ static void hloop_cleanup(hloop_t* loop) { hmutex_destroy(&loop->custom_events_mutex); } +static inline hio_t* create_client_impl(hloop_t* loop, sockaddr_u* peeraddr, int type, + hconnect_cb connect_cb, sockaddr_u* localaddr) { + int connfd = socket(peeraddr->sa.sa_family, type, 0); + if (connfd < 0) { + perror("socket"); + return NULL; + } + + hio_t* io = hio_get(loop, connfd); + if (io == NULL) return NULL; + hio_set_peeraddr(io, &peeraddr->sa, sockaddrlen(peeraddr)); +#ifdef HAVE_UDS + if (localaddr) { + if (bind(connfd, &localaddr->sa, sockaddrlen(localaddr)) < 0) { + perror("bind"); + hio_free(io); + return NULL; + } + hio_set_localaddr(io, &localaddr->sa, sockaddrlen(localaddr)); + } +#endif + if (type == SOCK_STREAM) { + hconnect(loop, connfd, connect_cb); + } + return io; +} + hloop_t* hloop_new(int flags) { hloop_t* loop; SAFE_ALLOC_SIZEOF(loop); @@ -703,17 +730,7 @@ hio_t* create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb //printf("unknown host: %s\n", host); return NULL; } - int connfd = socket(peeraddr.sa.sa_family, SOCK_STREAM, 0); - if (connfd < 0) { - perror("socket"); - return NULL; - } - - hio_t* io = hio_get(loop, connfd); - if (io == NULL) return NULL; - hio_set_peeraddr(io, &peeraddr.sa, sockaddrlen(&peeraddr)); - hconnect(loop, connfd, connect_cb); - return io; + return create_client_impl(loop, &peeraddr, SOCK_STREAM, connect_cb, NULL); } // @server: socket -> bind -> hrecvfrom @@ -735,19 +752,73 @@ hio_t* create_udp_client(hloop_t* loop, const char* host, int port) { //printf("unknown host: %s\n", host); return NULL; } + return create_client_impl(loop, &peeraddr, SOCK_DGRAM, NULL, NULL); +} - int sockfd = socket(peeraddr.sa.sa_family, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("socket"); +#ifdef HAVE_UDS + +hio_t* create_unix_stream_server(hloop_t* loop, const char* path, haccept_cb accept_cb) { + int listenfd = ListenUnix(path); + if (listenfd < 0) { return NULL; } - - hio_t* io = hio_get(loop, sockfd); - if (io == NULL) return NULL; - hio_set_peeraddr(io, &peeraddr.sa, sockaddrlen(&peeraddr)); + hio_t* io = haccept(loop, listenfd, accept_cb); + if (io == NULL) { + closesocket(listenfd); + } return io; } +hio_t* create_unix_stream_client(hloop_t* loop, const char* dest_path, const char* src_path, hconnect_cb connect_cb) { + sockaddr_u peeraddr, localaddr, *localaddr_p = NULL; + socklen_t addrlen = sizeof(peeraddr); + memset(&peeraddr, 0, addrlen); + int ret = sockaddr_unix_assign(&peeraddr, dest_path); + if (ret != 0) { + printf("socket path too long: %s", dest_path); + return NULL; + } + if (src_path) { + ret = sockaddr_unix_assign(&localaddr, src_path); + if (ret != 0) { + printf("socket path too long: %s", src_path); + return NULL; + } + localaddr_p = &localaddr; + } + return create_client_impl(loop, &peeraddr, SOCK_STREAM, connect_cb, localaddr_p); +} + +hio_t* create_unix_dgram_server(hloop_t* loop, const char* path) { + int bindfd = BindUnix(path, SOCK_DGRAM); + if (bindfd < 0) { + return NULL; + } + return hio_get(loop, bindfd); +} + +hio_t* create_unix_dgram_client(hloop_t* loop, const char* dest_path, const char* src_path) { + sockaddr_u peeraddr, localaddr, *localaddr_p = NULL; + socklen_t addrlen = sizeof(peeraddr); + memset(&peeraddr, 0, addrlen); + int ret = sockaddr_unix_assign(&peeraddr, dest_path); + if (ret != 0) { + printf("socket path too long: %s", dest_path); + return NULL; + } + if (src_path) { + ret = sockaddr_unix_assign(&localaddr, src_path); + if (ret != 0) { + printf("socket path too long: %s", src_path); + return NULL; + } + localaddr_p = &localaddr; + } + return create_client_impl(loop, &peeraddr, SOCK_DGRAM, NULL, localaddr_p); +} + +#endif + static void sockpair_read_cb(hio_t* io, void* buf, int readbytes) { hloop_t* loop = io->loop; hevent_t* pev = NULL; diff --git a/event/hloop.h b/event/hloop.h index d5f664725..f2227520a 100644 --- a/event/hloop.h +++ b/event/hloop.h @@ -233,6 +233,14 @@ hio_t* create_udp_server (hloop_t* loop, const char* host, int port); // @udp_client: resolver -> socket -> hio_get -> hio_set_peeraddr hio_t* create_udp_client (hloop_t* loop, const char* host, int port); +#ifdef HAVE_UDS +// Unix domain socket server/client +hio_t* create_unix_stream_server (hloop_t* loop, const char* path, haccept_cb accept_cb); +hio_t* create_unix_stream_client (hloop_t* loop, const char* dest_path, const char* src_path, hconnect_cb connect_cb); +hio_t* create_unix_dgram_server (hloop_t* loop, const char* path); +hio_t* create_unix_dgram_client (hloop_t* loop, const char* dest_path, const char* src_path); +#endif + END_EXTERN_C #endif // HV_LOOP_H_ diff --git a/event/nio.c b/event/nio.c index b699f7547..342bd9cdc 100644 --- a/event/nio.c +++ b/event/nio.c @@ -324,7 +324,7 @@ static void connect_timeout_cb(htimer_t* timer) { } int hio_connect(hio_t* io) { - int ret = connect(io->fd, io->peeraddr, sizeof(sockaddr_u)); + int ret = connect(io->fd, io->peeraddr, sockaddrlen((sockaddr_u*)io->peeraddr)); #ifdef OS_WIN if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) { #else @@ -370,7 +370,7 @@ int hio_write (hio_t* io, const void* buf, size_t len) { break; case HIO_TYPE_UDP: case HIO_TYPE_IP: - nwrite = sendto(io->fd, buf, len, 0, io->peeraddr, sizeof(sockaddr_u)); + nwrite = sendto(io->fd, buf, len, 0, io->peeraddr, sockaddrlen((sockaddr_u*)io->peeraddr)); break; default: nwrite = write(io->fd, buf, len); diff --git a/examples/udp.c b/examples/dgram_server.c similarity index 55% rename from examples/udp.c rename to examples/dgram_server.c index 3153e467d..d16a266f7 100644 --- a/examples/udp.c +++ b/examples/dgram_server.c @@ -1,6 +1,16 @@ #include "hloop.h" #include "hsocket.h" +const char* path = NULL; + +void cleanup(int signo) { + if (path) { + printf("cleaning up: %s\n", path); + unlink(path); + } + exit(0); +} + void on_close(hio_t* io) { printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); } @@ -19,20 +29,37 @@ void on_recvfrom(hio_t* io, void* buf, int readbytes) { } int main(int argc, char** argv) { - if (argc < 2) { - printf("Usage: cmd port\n"); - return -10; + int port; + if (argc == 2) { + port = atoi(argv[1]); } - int port = atoi(argv[1]); + else if (argc == 3 && strcmp(argv[1], "--unix") == 0) { + path = argv[2]; + } + else { + printf("Usage: cmd port\n" + " cmd --unix path\n"); + return -10; + }; hloop_t* loop = hloop_new(0); - hio_t* io = create_udp_server(loop, "0.0.0.0", port); - if (io == NULL) { - return -20; + hio_t* io = NULL; + if (path) { +#ifdef HAVE_UDS + io = create_unix_dgram_server(loop, path); +#else + printf("Unix domain socket is not supported!\n"); +#endif + } else { + io = create_udp_server(loop, "0.0.0.0", port); } hio_setcb_close(io, on_close); hio_setcb_read(io, on_recvfrom); hio_read(io); + + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + hloop_run(loop); hloop_free(&loop); return 0; diff --git a/examples/nc.c b/examples/nc.c index 9280f2988..53c9121be 100644 --- a/examples/nc.c +++ b/examples/nc.c @@ -12,7 +12,17 @@ hio_t* stdinio = NULL; // for socket hio_t* sockio = NULL; -int verbose = 0; +const char* src_path = NULL; + +int verbose = 1; + +void cleanup(int signo) { + if (protocol > 2) { + printf("cleaning up: %s\n", src_path); + unlink(src_path); + } + exit(0); +} void on_recv(hio_t* io, void* buf, int readbytes) { //printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes); @@ -66,34 +76,64 @@ void on_connect(hio_t* io) { } int main(int argc, char** argv) { - if (argc < 3) { - printf("\ -Usage: cmd [-ut] host port\n\ -Options:\n\ - -t Use tcp protocol (default)\n\ - -u Use udp protocol\n\ -Examples: nc 127.0.0.1 80\n\ - nc -u 127.0.0.1 80\n"); - return -10; + const char* protocol_name; + const char* host; + int port; + const char* dest_path; + if (argc == 3) { + protocol = 1; + protocol_name = "tcp"; + host = argv[1]; + port = atoi(argv[2]); } - - int index = 1; - const char* protocolname; - if (argv[1][0] == '-') { - ++index; - if (argv[1][1] == 't') { - protocol = 1; - protocolname = "tcp"; + else if (argc == 4) { + if (strcmp(argv[1], "-t") == 0 || strcmp(argv[1], "-u") == 0) { + if (argv[1][1] == 't') { + protocol = 1; + protocol_name = "tcp"; + } else { + protocol = 2; + protocol_name = "udp"; + } + host = argv[2]; + port = atoi(argv[3]); + } + else if (strcmp(argv[1], "-U") == 0 || strcmp(argv[1], "-D") == 0) { + if (argv[1][1] == 'U') { + protocol = 3; + protocol_name = "unix(stream)"; + } else { + protocol = 4; + protocol_name = "unix(datagram)"; + } + src_path = argv[2]; + dest_path = argv[3]; } - else if (argv[1][1] == 'u') { - protocol = 2; - protocolname = "udp"; + else { + goto bad_args; } } - const char* host = argv[index++]; - int port = atoi(argv[index++]); + else { +bad_args: + printf("Usage: cmd [-ut] host port\n" + " cmd [-UD] source dest\n" + "Options:\n" + " -t Use tcp protocol (default)\n" + " -u Use udp protocol\n" + " -U Use Unix domain socket (SOCK_STREAM)\n" + " -D Use Unix domain socket (SOCK_DGRAM)\n" + "Examples: nc 127.0.0.1 80\n" + " nc -u 127.0.0.1 80\n" + " nc -D ~/src.sock ~/dest.sock\n"); + return -10; + } if (verbose) { - printf("%s %s %d\n", protocolname, host, port); + if (protocol > 2) { + printf("%s: %s -> %s\n", protocol_name, src_path, dest_path); + } + else { + printf("%s: %s:%d\n", protocol_name, host, port); + } } MEMCHECK; @@ -116,6 +156,20 @@ Examples: nc 127.0.0.1 80\n\ sockio = create_udp_client(loop, host, port); hio_read(sockio); } +#ifdef HAVE_UDS + else if (protocol == 3) { + // Unix domain socket (SOCK_STREAM) + sockio = create_unix_stream_client(loop, dest_path, src_path, on_connect); + } + else if (protocol == 4) { + // Unix domain socket (SOCK_DGRAM) + sockio = create_unix_dgram_client(loop, dest_path, src_path); + } +#else + else { + printf("Unix domain socket is not supported!\n"); + } +#endif if (sockio == NULL) { return -20; } @@ -124,8 +178,14 @@ Examples: nc 127.0.0.1 80\n\ hio_setcb_read(sockio, on_recv); hio_set_readbuf(sockio, recvbuf, RECV_BUFSIZE); + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + hloop_run(loop); hloop_free(&loop); + if (protocol > 2) { + unlink(src_path); + } return 0; } diff --git a/examples/tcp.c b/examples/stream_server.c similarity index 63% rename from examples/tcp.c rename to examples/stream_server.c index f2b29e585..cc32bc65b 100644 --- a/examples/tcp.c +++ b/examples/stream_server.c @@ -1,6 +1,16 @@ #include "hloop.h" #include "hsocket.h" +const char* path = NULL; + +void cleanup(int signo) { + if (path) { + printf("cleaning up: %s\n", path); + unlink(path); + } + exit(0); +} + void on_close(hio_t* io) { printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); } @@ -32,18 +42,38 @@ void on_accept(hio_t* io) { } int main(int argc, char** argv) { - if (argc < 2) { - printf("Usage: cmd port\n"); + int port; + if (argc == 2) { + port = atoi(argv[1]); + } + else if (argc == 3 && strcmp(argv[1], "--unix") == 0) { + path = argv[2]; + } + else { + printf("Usage: cmd port\n" + " cmd --unix path\n"); return -10; } - int port = atoi(argv[1]); - + hloop_t* loop = hloop_new(0); - hio_t* listenio = create_tcp_server(loop, "0.0.0.0", port, on_accept); + hio_t* listenio = NULL; + if (path) { +#ifdef HAVE_UDS + listenio = create_unix_stream_server(loop, path, on_accept); +#else + printf("Unix domain socket is not supported!\n"); +#endif + } else { + listenio = create_tcp_server(loop, "0.0.0.0", port, on_accept); + } if (listenio == NULL) { return -20; } printf("listenfd=%d\n", hio_fd(listenio)); + + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + hloop_run(loop); hloop_free(&loop); return 0; diff --git a/readme_cn.md b/readme_cn.md index 6b0980780..61c0515b2 100644 --- a/readme_cn.md +++ b/readme_cn.md @@ -153,12 +153,19 @@ int main(int argc, char** argv) { } ``` ```shell -make tcp udp nc -bin/tcp 1111 +make stream_server dgram_server nc +# TCP server/client +bin/stream_server 1111 bin/nc 127.0.0.1 1111 - -bin/udp 2222 +# UDP server/client +bin/dgram_server 2222 bin/nc -u 127.0.0.1 2222 +# Unix stream socket server/client +bin/stream_server --unix ~/server.sock +bin/nc -U ~/client.sock ~/server.sock +# Unix datagram socket server/client +bin/dgram_server --unix ~/server.sock +bin/nc -D ~/client.sock ~/server.sock ``` ## 构建 @@ -172,8 +179,8 @@ bin/nc -u 127.0.0.1 2222 - make test # master-workers model - make timer # timer add/del/reset - make loop # event-loop(include idle, timer, io) - - make tcp # tcp server - - make udp # udp server + - make stream_server # stream socket server (TCP, Unix domain socket) + - make dgram_server # datagram socket server (UDP, Unix domain socket) - make nc # network client - make nmap # host discovery - make httpd # http server