1
0

hsocket.c 6.8 KB

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