hsocket.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #include "hsocket.h"
  2. #include "htime.h"
  3. #include "netinet.h"
  4. int Listen(int port) {
  5. // socket -> setsockopt -> bind -> listen
  6. int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  7. if (listenfd < 0) {
  8. perror("socket");
  9. return -sockerrno;
  10. }
  11. struct sockaddr_in localaddr;
  12. socklen_t addrlen = sizeof(localaddr);
  13. // NOTE: SO_REUSEADDR means that you can reuse sockaddr of TIME_WAIT status
  14. int reuseaddr = 1;
  15. if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(int)) < 0) {
  16. perror("setsockopt");
  17. goto error;
  18. }
  19. memset(&localaddr, 0, addrlen);
  20. localaddr.sin_family = AF_INET;
  21. localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  22. localaddr.sin_port = htons(port);
  23. if (bind(listenfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
  24. perror("bind");
  25. goto error;
  26. }
  27. if (listen(listenfd, SOMAXCONN) < 0) {
  28. perror("listen");
  29. goto error;
  30. }
  31. return listenfd;
  32. error:
  33. closesocket(listenfd);
  34. return sockerrno == 0 ? -1 : -sockerrno;
  35. }
  36. int Connect(const char* host, int port, int nonblock) {
  37. // gethostbyname -> socket -> nonblocking -> connect
  38. struct sockaddr_in peeraddr;
  39. socklen_t addrlen = sizeof(peeraddr);
  40. memset(&peeraddr, 0, addrlen);
  41. peeraddr.sin_family = AF_INET;
  42. inet_pton(peeraddr.sin_family, host, &peeraddr.sin_addr);
  43. if (peeraddr.sin_addr.s_addr == 0 ||
  44. peeraddr.sin_addr.s_addr == INADDR_NONE) {
  45. struct hostent* phe = gethostbyname(host);
  46. if (phe == NULL) return -h_errno;
  47. peeraddr.sin_family = phe->h_addrtype;
  48. memcpy(&peeraddr.sin_addr, phe->h_addr_list[0], phe->h_length);
  49. }
  50. peeraddr.sin_port = htons(port);
  51. int connfd = socket(AF_INET, SOCK_STREAM, 0);
  52. if (connfd < 0) {
  53. perror("socket");
  54. return -sockerrno;
  55. }
  56. if (nonblock) {
  57. nonblocking(connfd);
  58. }
  59. int ret = connect(connfd, (struct sockaddr*)&peeraddr, addrlen);
  60. #ifdef OS_WIN
  61. if (ret < 0 && sockerrno != WSAEWOULDBLOCK) {
  62. #else
  63. if (ret < 0 && sockerrno != EINPROGRESS) {
  64. #endif
  65. perror("connect");
  66. goto error;
  67. }
  68. return connfd;
  69. error:
  70. closesocket(connfd);
  71. return sockerrno == 0 ? -1 : -sockerrno;
  72. }
  73. #define PING_TIMEOUT 1000 // ms
  74. int Ping(const char* host, int cnt) {
  75. static uint16_t seq = 0;
  76. char ip[64] = {0};
  77. uint64_t start_tick, end_tick;
  78. uint64_t start_hrtime, end_hrtime;
  79. int timeout = 0;
  80. int sendbytes = 64;
  81. char sendbuf[64];
  82. char recvbuf[128]; // iphdr + icmp = 84 at least
  83. struct icmp* icmp_req = (struct icmp*)sendbuf;
  84. struct iphdr* ipheader = (struct iphdr*)recvbuf;
  85. struct icmp* icmp_res;
  86. // ping stat
  87. int send_cnt = 0;
  88. int recv_cnt = 0;
  89. int ok_cnt = 0;
  90. float rtt, min_rtt, max_rtt, total_rtt;
  91. rtt = max_rtt = total_rtt = 0.0f;
  92. min_rtt = 1000000.0f;
  93. //min_rtt = MIN(rtt, min_rtt);
  94. //max_rtt = MAX(rtt, max_rtt);
  95. // gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
  96. struct sockaddr_in peeraddr;
  97. socklen_t addrlen = sizeof(peeraddr);
  98. memset(&peeraddr, 0, addrlen);
  99. peeraddr.sin_family = AF_INET;
  100. inet_pton(peeraddr.sin_family, host, &peeraddr.sin_addr);
  101. if (peeraddr.sin_addr.s_addr == 0 ||
  102. peeraddr.sin_addr.s_addr == INADDR_NONE) {
  103. struct hostent* phe = gethostbyname(host);
  104. if (phe == NULL) {
  105. printd("unknown host %s\n", host);
  106. return -h_errno;
  107. }
  108. peeraddr.sin_family = phe->h_addrtype;
  109. memcpy(&peeraddr.sin_addr, phe->h_addr_list[0], phe->h_length);
  110. }
  111. inet_ntop(peeraddr.sin_family, &peeraddr.sin_addr, ip, sizeof(ip));
  112. int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  113. if (sockfd < 0) {
  114. perror("socket");
  115. if (errno == EPERM) {
  116. fprintf(stderr, "please use root or sudo to create a raw socket.\n");
  117. }
  118. return -sockerrno;
  119. }
  120. timeout = PING_TIMEOUT;
  121. int ret = so_sndtimeo(sockfd, timeout);
  122. if (ret < 0) {
  123. perror("setsockopt");
  124. goto error;
  125. }
  126. timeout = PING_TIMEOUT;
  127. ret = so_rcvtimeo(sockfd, timeout);
  128. if (ret < 0) {
  129. perror("setsockopt");
  130. goto error;
  131. }
  132. icmp_req->icmp_type = ICMP_ECHO;
  133. icmp_req->icmp_code = 0;
  134. icmp_req->icmp_id = getpid();
  135. for (int i = 0; i < sendbytes - sizeof(struct icmphdr); ++i) {
  136. icmp_req->icmp_data[i] = i;
  137. }
  138. start_tick = gettick();
  139. while (cnt-- > 0) {
  140. // NOTE: checksum
  141. icmp_req->icmp_cksum = 0;
  142. icmp_req->icmp_seq = ++seq;
  143. icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
  144. start_hrtime = gethrtime();
  145. int nsend = sendto(sockfd, sendbuf, sendbytes, 0, (struct sockaddr*)&peeraddr, addrlen);
  146. if (nsend < 0) {
  147. perror("sendto");
  148. continue;
  149. }
  150. ++send_cnt;
  151. addrlen = sizeof(peeraddr);
  152. int nrecv = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &addrlen);
  153. if (nrecv < 0) {
  154. perror("recvfrom");
  155. continue;
  156. }
  157. ++recv_cnt;
  158. end_hrtime = gethrtime();
  159. // check valid
  160. bool valid = false;
  161. int iphdr_len = ipheader->ihl * 4;
  162. int icmp_len = nrecv - iphdr_len;
  163. if (icmp_len == sendbytes) {
  164. icmp_res = (struct icmp*)(recvbuf + ipheader->ihl*4);
  165. if (icmp_res->icmp_type == ICMP_ECHOREPLY &&
  166. icmp_res->icmp_id == getpid() &&
  167. icmp_res->icmp_seq == seq) {
  168. valid = true;
  169. }
  170. }
  171. if (valid == false) {
  172. printd("recv invalid icmp packet!\n");
  173. continue;
  174. }
  175. rtt = (end_hrtime-start_hrtime) / 1000.0f;
  176. min_rtt = MIN(rtt, min_rtt);
  177. max_rtt = MAX(rtt, max_rtt);
  178. total_rtt += rtt;
  179. printd("%d bytes from %s: icmp_seq=%u ttl=%u time=%.1f ms\n", icmp_len, ip, seq, ipheader->ttl, rtt);
  180. fflush(stdout);
  181. ++ok_cnt;
  182. if (cnt > 0) sleep(1); // sleep a while, then agian
  183. }
  184. end_tick = gettick();
  185. printd("--- %s ping statistics ---\n", host);
  186. printd("%d packets transmitted, %d received, %d%% packet loss, time %d ms\n",
  187. send_cnt, recv_cnt, (send_cnt-recv_cnt)*100/(send_cnt==0?1:send_cnt), end_tick-start_tick);
  188. printd("rtt min/avg/max = %.3f/%.3f/%.3f ms\n",
  189. min_rtt, total_rtt/(ok_cnt==0?1:ok_cnt), max_rtt);
  190. closesocket(sockfd);
  191. return ok_cnt;
  192. error:
  193. closesocket(sockfd);
  194. return sockerrno == 0 ? -1 : -sockerrno;
  195. }