Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
12 changes: 12 additions & 0 deletions base/hplatform.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@
#include <direct.h> // for mkdir,rmdir,chdir,getcwd
#include <io.h> // for open,close,read,write,lseek,tell

#ifdef ENABLE_UDS
// TODO: support Unix domain socket on Windows
// #define HAVE_UDS
// #include <afunix.h>
#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)
Expand All @@ -147,6 +154,11 @@
#include <netinet/udp.h>
#include <netdb.h> // for gethostbyname

#ifdef ENABLE_UDS
#define HAVE_UDS
#include <sys/un.h> // For Unix domain sockets
#endif

#define MKDIR(dir) mkdir(dir, 0777)
#endif

Expand Down
188 changes: 125 additions & 63 deletions base/hsocket.c
Original file line number Diff line number Diff line change
@@ -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];
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down
37 changes: 37 additions & 0 deletions base/hsocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

Expand Down Expand Up @@ -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};
Expand All @@ -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;
}

Expand All @@ -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
Expand All @@ -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]);

Expand Down
Loading