ithewei 6 rokov pred
rodič
commit
09517c6b49
8 zmenil súbory, kde vykonal 420 pridanie a 42 odobranie
  1. 6 2
      Makefile
  2. 162 28
      base/hsocket.c
  3. 27 0
      base/hsocket.h
  4. 3 1
      base/htime.c
  5. 7 1
      base/htime.h
  6. 192 0
      base/netinet.h
  7. 9 10
      event/overlapio.c
  8. 14 0
      examples/ping.c

+ 6 - 2
Makefile

@@ -3,7 +3,7 @@ TMPDIR=tmp
 
 default: all
 
-all: test loop client server httpd
+all: test ping loop client server httpd
 
 clean:
 	$(MAKEF) clean SRCDIRS=". base utils event http http/client http/server examples $(TMPDIR)"
@@ -16,6 +16,10 @@ test: prepare
 	cp main.cpp.tmpl $(TMPDIR)/main.cpp
 	$(MAKEF) TARGET=$@ SRCDIRS=". base utils $(TMPDIR)"
 
+ping:
+	-rm base/hsocket.o
+	$(MAKEF) TARGET=$@ SRCDIRS="" SRCS="examples/ping.c base/hsocket.c base/htime.c base/RAII.cpp" INCDIRS="base" DEFINES="PRINT_DEBUG"
+
 loop: prepare
 	-rm $(TMPDIR)/*.o $(TMPDIR)/*.h $(TMPDIR)/*.c $(TMPDIR)/*.cpp
 	cp examples/loop.c $(TMPDIR)/loop.c
@@ -48,4 +52,4 @@ CURL_SRCS    += examples/curl.cpp base/hstring.cpp
 curl:
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CURL_SRCDIRS)" INCDIRS="$(CURL_INCDIRS)" SRCS="$(CURL_SRCS)" DEFINES="CURL_STATICLIB" LIBS="curl"
 
-.PHONY: clean prepare test loop client server httpd webbench curl
+.PHONY: clean prepare test ping loop client server httpd webbench curl

+ 162 - 28
base/hsocket.c

@@ -1,68 +1,202 @@
 #include "hsocket.h"
+#include "htime.h"
+#include "netinet.h"
 
 int Listen(int port) {
     // socket -> setsockopt -> bind -> listen
     int listenfd = socket(AF_INET, SOCK_STREAM, 0);
     if (listenfd < 0) {
         perror("socket");
-        return -10;
+        return -sockerrno;
     }
-    // note: SO_REUSEADDR means that you can reuse sockaddr of TIME_WAIT status
+    struct sockaddr_in localaddr;
+    socklen_t addrlen = sizeof(localaddr);
+    // NOTE: SO_REUSEADDR means that you can reuse sockaddr of TIME_WAIT status
     int reuseaddr = 1;
     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(int)) < 0) {
         perror("setsockopt");
-        closesocket(listenfd);
-        return -11;
+        goto error;
     }
-    struct sockaddr_in addr;
-    socklen_t addrlen = sizeof(addr);
-    memset(&addr, 0, addrlen);
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    addr.sin_port = htons(port);
-    if (bind(listenfd, (struct sockaddr*)&addr, addrlen) < 0) {
+    memset(&localaddr, 0, addrlen);
+    localaddr.sin_family = AF_INET;
+    localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    localaddr.sin_port = htons(port);
+    if (bind(listenfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
         perror("bind");
-        closesocket(listenfd);
-        return -20;
+        goto error;
     }
     if (listen(listenfd, SOMAXCONN) < 0) {
         perror("listen");
-        closesocket(listenfd);
-        return -30;
+        goto error;
     }
     return listenfd;
+error:
+    closesocket(listenfd);
+    return sockerrno == 0 ? -1 : -sockerrno;
 }
 
 int Connect(const char* host, int port, int nonblock) {
     // gethostbyname -> socket -> nonblocking -> connect
-    struct sockaddr_in addr;
-    socklen_t addrlen = sizeof(addr);
-    memset(&addr, 0, addrlen);
-    addr.sin_family = AF_INET;
-    inet_pton(AF_INET, host, &addr.sin_addr);
-    if (addr.sin_addr.s_addr == INADDR_NONE) {
+    struct sockaddr_in peeraddr;
+    socklen_t addrlen = sizeof(peeraddr);
+    memset(&peeraddr, 0, addrlen);
+    peeraddr.sin_family = AF_INET;
+    inet_pton(peeraddr.sin_family, host, &peeraddr.sin_addr);
+    if (peeraddr.sin_addr.s_addr == 0 ||
+        peeraddr.sin_addr.s_addr == INADDR_NONE) {
         struct hostent* phe = gethostbyname(host);
-        if (phe == NULL)    return -10;
-        memcpy(&addr.sin_addr, phe->h_addr, phe->h_length);
+        if (phe == NULL)    return -h_errno;
+        peeraddr.sin_family = phe->h_addrtype;
+        memcpy(&peeraddr.sin_addr, phe->h_addr_list[0], phe->h_length);
     }
-    addr.sin_port = htons(port);
+    peeraddr.sin_port = htons(port);
     int connfd = socket(AF_INET, SOCK_STREAM, 0);
     if (connfd < 0) {
         perror("socket");
-        return -20;
+        return -sockerrno;
     }
     if (nonblock) {
         nonblocking(connfd);
     }
-    int ret = connect(connfd, (struct sockaddr*)&addr, addrlen);
+    int ret = connect(connfd, (struct sockaddr*)&peeraddr, addrlen);
 #ifdef OS_WIN
     if (ret < 0 && sockerrno != WSAEWOULDBLOCK) {
 #else
     if (ret < 0 && sockerrno != EINPROGRESS) {
 #endif
         perror("connect");
-        closesocket(connfd);
-        return -30;
+        goto error;
     }
     return connfd;
+error:
+    closesocket(connfd);
+    return sockerrno == 0 ? -1 : -sockerrno;
+}
+
+#define PING_TIMEOUT    1000 // ms
+int Ping(const char* host, int cnt) {
+    static uint16_t seq = 0;
+    char ip[64] = {0};
+    int ttl = 0;
+    uint64_t start_tick, end_tick;
+    uint64_t start_hrtime, end_hrtime;
+    int timeout = 0;
+    int sendbytes = 64;
+    char sendbuf[64];
+    char recvbuf[128]; // iphdr + icmp = 84 at least
+    struct icmp* icmp_req = (struct icmp*)sendbuf;
+    struct iphdr* ipheader = (struct iphdr*)recvbuf;
+    struct icmp* icmp_res;
+    // ping stat
+    int send_cnt = 0;
+    int recv_cnt = 0;
+    int ok_cnt = 0;
+    float rtt, min_rtt, max_rtt, total_rtt;
+    rtt = max_rtt = total_rtt = 0.0f;
+    min_rtt = 1000000.0f;
+    //min_rtt = MIN(rtt, min_rtt);
+    //max_rtt = MAX(rtt, max_rtt);
+    // gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
+    struct sockaddr_in peeraddr;
+    socklen_t addrlen = sizeof(peeraddr);
+    memset(&peeraddr, 0, addrlen);
+    peeraddr.sin_family = AF_INET;
+    inet_pton(peeraddr.sin_family, host, &peeraddr.sin_addr);
+    if (peeraddr.sin_addr.s_addr == 0 ||
+        peeraddr.sin_addr.s_addr == INADDR_NONE) {
+        struct hostent* phe = gethostbyname(host);
+        if (phe == NULL) {
+            printd("unknown host %s\n", host);
+            return -h_errno;
+        }
+        peeraddr.sin_family = phe->h_addrtype;
+        memcpy(&peeraddr.sin_addr, phe->h_addr_list[0], phe->h_length);
+    }
+    inet_ntop(peeraddr.sin_family, &peeraddr.sin_addr, ip, sizeof(ip));
+    int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+    if (sockfd < 0) {
+        perror("socket");
+        if (errno == EPERM) {
+            fprintf(stderr, "please use root or sudo to create a raw socket.\n");
+        }
+        return -sockerrno;
+    }
+
+    timeout = PING_TIMEOUT;
+    int ret = so_sndtimeo(sockfd, timeout);
+    if (ret < 0) {
+        perror("setsockopt");
+        goto error;
+    }
+    timeout = PING_TIMEOUT;
+    ret = so_rcvtimeo(sockfd, timeout);
+    if (ret < 0) {
+        perror("setsockopt");
+        goto error;
+    }
+
+    icmp_req->icmp_type = ICMP_ECHO;
+    icmp_req->icmp_code = 0;
+    icmp_req->icmp_id = getpid();
+    for (int i = 0; i < sendbytes - sizeof(struct icmphdr); ++i) {
+        icmp_req->icmp_data[i] = i;
+    }
+    start_tick = gettick();
+    while (cnt-- > 0) {
+        // NOTE: checksum
+        icmp_req->icmp_cksum = 0;
+        icmp_req->icmp_seq = ++seq;
+        icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
+        start_hrtime = gethrtime();
+        int nsend = sendto(sockfd, sendbuf, sendbytes, 0, (struct sockaddr*)&peeraddr, addrlen);
+        if (nsend < 0) {
+            perror("sendto");
+            continue;
+        }
+        ++send_cnt;
+        addrlen = sizeof(peeraddr);
+        int nrecv = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &addrlen);
+        if (nrecv < 0) {
+            perror("recvfrom");
+            continue;
+        }
+        ++recv_cnt;
+        end_hrtime = gethrtime();
+        // check valid
+        bool valid = false;
+        int iphdr_len = ipheader->ihl * 4;
+        int icmp_len = nrecv - iphdr_len;
+        if (icmp_len == sendbytes) {
+            icmp_res = (struct icmp*)(recvbuf + ipheader->ihl*4);
+            if (icmp_res->icmp_type == ICMP_ECHOREPLY &&
+                icmp_res->icmp_id == getpid() &&
+                icmp_res->icmp_seq == seq) {
+                valid = true;
+            }
+        }
+        if (valid == false) {
+            printd("recv invalid icmp packet!\n");
+            continue;
+        }
+        rtt = (end_hrtime-start_hrtime) / 1000.0f;
+        min_rtt = MIN(rtt, min_rtt);
+        max_rtt = MAX(rtt, max_rtt);
+        total_rtt += rtt;
+        printd("%d bytes from %s: icmp_seq=%u ttl=%u time=%.1f ms\n", icmp_len, ip, seq, ipheader->ttl, rtt);
+        fflush(stdout);
+        ++ok_cnt;
+        if (cnt > 0) sleep(1); // sleep a while, then agian
+    }
+    end_tick = gettick();
+    printd("--- %s ping statistics ---\n", host);
+    printd("%d packets transmitted, %d received, %d%% packet loss, time %d ms\n",
+        send_cnt, recv_cnt, (send_cnt-recv_cnt)*100/(send_cnt==0?1:send_cnt), end_tick-start_tick);
+    printd("rtt min/avg/max = %.3f/%.3f/%.3f ms\n",
+        min_rtt, total_rtt/(ok_cnt==0?1:ok_cnt), max_rtt);
+
+    closesocket(sockfd);
+    return ok_cnt;
+error:
+    closesocket(sockfd);
+    return sockerrno == 0 ? -1 : -sockerrno;
 }

+ 27 - 0
base/hsocket.h

@@ -18,6 +18,11 @@ int Listen(int port);
 // @return sockfd
 int Connect(const char* host, int port, int nonblock DEFAULT(0));
 
+// @param cnt: ping count
+// @return: ok count
+// @note: printd $CC -DPRINT_DEBUG
+int Ping(const char* host, int cnt DEFAULT(4));
+
 #ifdef OS_WIN
 typedef int socklen_t;
 static inline int blocking(int sockfd) {
@@ -65,6 +70,8 @@ static inline int tcp_keepalive(int sockfd, int on DEFAULT(1), int delay DEFAULT
     // TCP_KEEPCNT      => tcp_keepalive_probes
     // TCP_KEEPINTVL    => tcp_keepalive_intvl
     return setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (const char*)&delay, sizeof(int));
+#else
+    return 0;
 #endif
 }
 
@@ -72,6 +79,26 @@ static inline int udp_broadcast(int sockfd, int on DEFAULT(1)) {
     return setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(int));
 }
 
+// send timeout
+static inline int so_sndtimeo(int sockfd, int timeout) {
+#ifdef OS_WIN
+    return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(int));
+#else
+    struct timeval tv = {timeout/1000, (timeout%1000)*1000};
+    return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+#endif
+}
+
+// recv timeout
+static inline int so_rcvtimeo(int sockfd, int timeout) {
+#ifdef OS_WIN
+    return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(int));
+#else
+    struct timeval tv = {timeout/1000, (timeout%1000)*1000};
+    return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+#endif
+}
+
 END_EXTERN_C
 
 #endif // HW_SOCKET_H_

+ 3 - 1
base/htime.c

@@ -36,7 +36,9 @@ inline unsigned long long gethrtime() {
     clock_gettime(CLOCK_MONOTONIC, &ts);
     return ts.tv_sec*(unsigned long long)1000000 + ts.tv_nsec / 1000;
 #else
-    return clock() / (double)CLOCKS_PER_SEC * 1000000;
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec*(unsigned long long)1000000 + tv.tv_usec;
 #endif
 }
 

+ 7 - 1
base/htime.h

@@ -43,8 +43,14 @@ static inline void msleep(unsigned int ms) {
 static inline unsigned int gettick() {
 #ifdef OS_WIN
     return GetTickCount();
+#elif defined(OS_LINUX)
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
 #else
-    return clock()*(unsigned long long)1000 / CLOCKS_PER_SEC;
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
 #endif
 }
 

+ 192 - 0
base/netinet.h

@@ -0,0 +1,192 @@
+#ifndef HW_NETINET_H_
+#define HW_NETINET_H_
+
+#include "hplatform.h"
+
+#ifdef OS_UNIX
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/ip_icmp.h>
+typedef struct iphdr    iphdr_t;
+typedef struct udphdr   udphdr_t;
+typedef struct tcphdr   tcphdr_t;
+
+typedef struct icmphdr  icmphdr_t;
+typedef struct icmp     icmp;
+#else
+// sizeof(iphdr_t) = 20
+typedef struct iphdr {
+#if BYTE_ORDER == LITTLE_ENDIAN
+    unsigned int ihl:4; // ip header length
+    unsigned int version:4;
+#elif BYTE_ORDER == BIG_ENDIAN
+    unsigned int version:4;
+    unsigned int ihl:4;
+#else
+#error "BYTE_ORDER undefined!"
+#endif
+    uint8_t     tos; // type of service
+    uint16_t    tot_len; // total length
+    uint16_t    id;
+    uint16_t    frag_off; // fragment offset
+    uint8_t     ttl; // Time To Live
+    uint8_t     protocol;
+    uint16_t    check; // checksum
+    uint32_t    saddr; // srcaddr
+    uint32_t    daddr; // dstaddr
+    /*The options start here.*/
+} iphdr_t;
+
+// sizeof(udphdr_t) = 8
+typedef struct udphdr {
+    uint16_t    source; // source port
+    uint16_t    dest;   // dest   port
+    uint16_t    len;    // udp length
+    uint16_t    check;  // checksum
+} udphdr_t;
+
+// sizeof(tcphdr_t) = 20
+typedef struct tcphdr {
+    uint16_t    source; // source port
+    uint16_t    dest;   // dest   port
+    uint32_t    seq;    // sequence
+    uint32_t    ack_seq;
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint16_t    res1:4;
+    uint16_t    doff:4;
+    uint16_t    fin:1;
+    uint16_t    syn:1;
+    uint16_t    rst:1;
+    uint16_t    psh:1;
+    uint16_t    ack:1;
+    uint16_t    urg:1;
+    uint16_t    res2:2;
+#elif BYTE_ORDER == BIG_ENDIAN
+    uint16_t    doff:4;
+    uint16_t    res1:4;
+    uint16_t    res2:2;
+    uint16_t    urg:1;
+    uint16_t    ack:1;
+    uint16_t    psh:1;
+    uint16_t    rst:1;
+    uint16_t    syn:1;
+    uint16_t    fin:1;
+#else
+#error "BYTE_ORDER undefined!"
+#endif
+    uint16_t    window;
+    uint16_t    check; // checksum
+    uint16_t    urg_ptr; // urgent pointer
+} tcphdr_t;
+
+//----------------------icmp----------------------------------
+#define ICMP_ECHOREPLY		0	/* Echo Reply			*/
+#define ICMP_DEST_UNREACH	3	/* Destination Unreachable	*/
+#define ICMP_SOURCE_QUENCH	4	/* Source Quench		*/
+#define ICMP_REDIRECT		5	/* Redirect (change route)	*/
+#define ICMP_ECHO		8	/* Echo Request			*/
+#define ICMP_TIME_EXCEEDED	11	/* Time Exceeded		*/
+#define ICMP_PARAMETERPROB	12	/* Parameter Problem		*/
+#define ICMP_TIMESTAMP		13	/* Timestamp Request		*/
+#define ICMP_TIMESTAMPREPLY	14	/* Timestamp Reply		*/
+#define ICMP_INFO_REQUEST	15	/* Information Request		*/
+#define ICMP_INFO_REPLY		16	/* Information Reply		*/
+#define ICMP_ADDRESS		17	/* Address Mask Request		*/
+#define ICMP_ADDRESSREPLY	18	/* Address Mask Reply		*/
+
+// sizeof(icmphdr_t) = 8
+typedef struct icmphdr {
+    uint8_t     type;   // message type
+    uint8_t     code;   // type sub-code
+    uint16_t    checksum;
+    union {
+        struct {
+            uint16_t    id;
+            uint16_t    sequence;
+        } echo;
+        uint32_t    gateway;
+        struct {
+            uint16_t    reserved;
+            uint16_t    mtu;
+        } frag;
+    }
+} icmphdr_t;
+
+typedef struct icmp {
+    uint8_t     icmp_type;
+    uint8_t     icmp_code;
+    uint16_t    icmp_cksum;
+    union {
+        uint8_t ih_pptr;
+        struct in_addr ih_gwaddr;
+        struct ih_idseq {
+            uint16_t icd_id;
+            uint16_t icd_seq;
+        } ih_idseq;
+        uint32_t    ih_void;
+
+        struct ih_pmtu {
+            uint16_t ipm_void;
+            uint16_t ipm_nextmtu;
+        } ih_pmtu;
+
+        struct ih_rtradv {
+            uint8_t irt_num_addrs;
+            uint8_t irt_wpa;
+            uint16_t irt_lifetime;
+        } ih_rtradv;
+    } icmp_hun;
+#define	icmp_pptr	icmp_hun.ih_pptr
+#define	icmp_gwaddr	icmp_hun.ih_gwaddr
+#define	icmp_id		icmp_hun.ih_idseq.icd_id
+#define	icmp_seq	icmp_hun.ih_idseq.icd_seq
+#define	icmp_void	icmp_hun.ih_void
+#define	icmp_pmvoid	icmp_hun.ih_pmtu.ipm_void
+#define	icmp_nextmtu	icmp_hun.ih_pmtu.ipm_nextmtu
+#define	icmp_num_addrs	icmp_hun.ih_rtradv.irt_num_addrs
+#define	icmp_wpa	icmp_hun.ih_rtradv.irt_wpa
+#define	icmp_lifetime	icmp_hun.ih_rtradv.irt_lifetime
+
+    union {
+        struct {
+            uint32_t its_otime;
+            uint32_t its_rtime;
+            uint32_t its_ttime;
+        } id_ts;
+        /*
+        struct {
+            struct ip idi_ip;
+        } id_ip;
+        struct icmp_ra_addr id_radv;
+        */
+        uint32_t id_mask;
+        uint8_t  id_data[1];
+    } icmp_dun;
+#define	icmp_otime	icmp_dun.id_ts.its_otime
+#define	icmp_rtime	icmp_dun.id_ts.its_rtime
+#define	icmp_ttime	icmp_dun.id_ts.its_ttime
+#define	icmp_ip		icmp_dun.id_ip.idi_ip
+#define	icmp_radv	icmp_dun.id_radv
+#define	icmp_mask	icmp_dun.id_mask
+#define	icmp_data	icmp_dun.id_data
+} icmp;
+#endif
+
+static inline uint16_t checksum(uint8_t* buf, int len) {
+    unsigned int sum = 0;
+    uint16_t* ptr = (uint16_t*)buf;
+    while(len > 1) {
+        sum += *ptr++;
+        len -= 2;
+    }
+    if(len) {
+        sum += *(uint8_t*)ptr;
+    }
+    sum = (sum >> 16) + (sum & 0xffff);
+    sum += (sum >> 16);
+    return (uint16_t)(~sum);
+};
+
+#endif // HW_NETINET_H_

+ 9 - 10
event/overlapio.c

@@ -239,10 +239,12 @@ hio_t* hconnect (hloop_t* loop, const char* host, int port, hconnect_cb connect_
     memset(&peeraddr, 0, addrlen);
     peeraddr.sin_family = AF_INET;
     inet_pton(peeraddr.sin_family, host, &peeraddr.sin_addr);
-    if (peeraddr.sin_addr.s_addr == INADDR_NONE) {
+    if (peeraddr.sin_addr.s_addr == 0 ||
+        peeraddr.sin_addr.s_addr == INADDR_NONE) {
         struct hostent* phe = gethostbyname(host);
         if (phe == NULL)    return NULL;
-        memcpy(&peeraddr.sin_addr, phe->h_addr, phe->h_length);
+        peeraddr.sin_family = phe->h_addrtype;
+        memcpy(&peeraddr.sin_addr, phe->h_addr_list[0], phe->h_length);
     }
     peeraddr.sin_port = htons(port);
     int connfd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
@@ -258,14 +260,12 @@ hio_t* hconnect (hloop_t* loop, const char* host, int port, hconnect_cb connect_
     localaddr.sin_port = htons(0);
     if (bind(connfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
         perror("bind");
-        closesocket(connfd);
-        return NULL;
+        goto error;
     }
     nonblocking(connfd);
     hio_t* io = hio_add(loop, hio_handle_events, connfd, WRITE_EVENT);
     if (io == NULL) {
-        closesocket(connfd);
-        return NULL;
+        goto error;
     }
     if (connect_cb) {
         io->connect_cb = connect_cb;
@@ -280,8 +280,7 @@ hio_t* hconnect (hloop_t* loop, const char* host, int port, hconnect_cb connect_
         &guidConnectEx, sizeof(guidConnectEx),
         &ConnectEx, sizeof(ConnectEx),
         &dwbytes, NULL, NULL) != 0) {
-        closesocket(connfd);
-        return NULL;
+        goto error;
     }
     // NOTE: free on_connectex_complete
     hoverlapped_t* hovlp = (hoverlapped_t*)malloc(sizeof(hoverlapped_t));
@@ -293,14 +292,14 @@ hio_t* hconnect (hloop_t* loop, const char* host, int port, hconnect_cb connect_
         int err = WSAGetLastError();
         if (err != ERROR_IO_PENDING) {
             fprintf(stderr, "AcceptEx error: %d\n", err);
-            return NULL;
+            goto error;
         }
     }
     return io;
 error:
     closesocket(connfd);
     return NULL;
-}
+};
 
 hio_t* hread (hloop_t* loop, int fd, void* buf, size_t len, hread_cb read_cb) {
     hio_t* io = hio_add(loop, hio_handle_events, fd, READ_EVENT);

+ 14 - 0
examples/ping.c

@@ -0,0 +1,14 @@
+#include "hsocket.h"
+
+int main(int argc, char* argv[]) {
+    if (argc < 2) {
+        printf("Usage: ping host|ip\n");
+        return -10;
+    }
+
+    char* host = argv[1];
+    int ping_cnt = 4;
+    int ok_cnt = Ping(host, ping_cnt);
+    printf("ping %d count, %d ok.\n", ping_cnt, ok_cnt);
+    return 0;
+}