1
0

hsocket.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. int ttl = 0;
  78. uint64_t start_tick, end_tick;
  79. uint64_t start_hrtime, end_hrtime;
  80. int timeout = 0;
  81. int sendbytes = 64;
  82. char sendbuf[64];
  83. char recvbuf[128]; // iphdr + icmp = 84 at least
  84. struct icmp* icmp_req = (struct icmp*)sendbuf;
  85. struct iphdr* ipheader = (struct iphdr*)recvbuf;
  86. struct icmp* icmp_res;
  87. // ping stat
  88. int send_cnt = 0;
  89. int recv_cnt = 0;
  90. int ok_cnt = 0;
  91. float rtt, min_rtt, max_rtt, total_rtt;
  92. rtt = max_rtt = total_rtt = 0.0f;
  93. min_rtt = 1000000.0f;
  94. //min_rtt = MIN(rtt, min_rtt);
  95. //max_rtt = MAX(rtt, max_rtt);
  96. // gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
  97. struct sockaddr_in peeraddr;
  98. socklen_t addrlen = sizeof(peeraddr);
  99. memset(&peeraddr, 0, addrlen);
  100. peeraddr.sin_family = AF_INET;
  101. inet_pton(peeraddr.sin_family, host, &peeraddr.sin_addr);
  102. if (peeraddr.sin_addr.s_addr == 0 ||
  103. peeraddr.sin_addr.s_addr == INADDR_NONE) {
  104. struct hostent* phe = gethostbyname(host);
  105. if (phe == NULL) {
  106. printd("unknown host %s\n", host);
  107. return -h_errno;
  108. }
  109. peeraddr.sin_family = phe->h_addrtype;
  110. memcpy(&peeraddr.sin_addr, phe->h_addr_list[0], phe->h_length);
  111. }
  112. inet_ntop(peeraddr.sin_family, &peeraddr.sin_addr, ip, sizeof(ip));
  113. int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  114. if (sockfd < 0) {
  115. perror("socket");
  116. if (errno == EPERM) {
  117. fprintf(stderr, "please use root or sudo to create a raw socket.\n");
  118. }
  119. return -sockerrno;
  120. }
  121. timeout = PING_TIMEOUT;
  122. int ret = so_sndtimeo(sockfd, timeout);
  123. if (ret < 0) {
  124. perror("setsockopt");
  125. goto error;
  126. }
  127. timeout = PING_TIMEOUT;
  128. ret = so_rcvtimeo(sockfd, timeout);
  129. if (ret < 0) {
  130. perror("setsockopt");
  131. goto error;
  132. }
  133. icmp_req->icmp_type = ICMP_ECHO;
  134. icmp_req->icmp_code = 0;
  135. icmp_req->icmp_id = getpid();
  136. for (int i = 0; i < sendbytes - sizeof(struct icmphdr); ++i) {
  137. icmp_req->icmp_data[i] = i;
  138. }
  139. start_tick = gettick();
  140. while (cnt-- > 0) {
  141. // NOTE: checksum
  142. icmp_req->icmp_cksum = 0;
  143. icmp_req->icmp_seq = ++seq;
  144. icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
  145. start_hrtime = gethrtime();
  146. int nsend = sendto(sockfd, sendbuf, sendbytes, 0, (struct sockaddr*)&peeraddr, addrlen);
  147. if (nsend < 0) {
  148. perror("sendto");
  149. continue;
  150. }
  151. ++send_cnt;
  152. addrlen = sizeof(peeraddr);
  153. int nrecv = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &addrlen);
  154. if (nrecv < 0) {
  155. perror("recvfrom");
  156. continue;
  157. }
  158. ++recv_cnt;
  159. end_hrtime = gethrtime();
  160. // check valid
  161. bool valid = false;
  162. int iphdr_len = ipheader->ihl * 4;
  163. int icmp_len = nrecv - iphdr_len;
  164. if (icmp_len == sendbytes) {
  165. icmp_res = (struct icmp*)(recvbuf + ipheader->ihl*4);
  166. if (icmp_res->icmp_type == ICMP_ECHOREPLY &&
  167. icmp_res->icmp_id == getpid() &&
  168. icmp_res->icmp_seq == seq) {
  169. valid = true;
  170. }
  171. }
  172. if (valid == false) {
  173. printd("recv invalid icmp packet!\n");
  174. continue;
  175. }
  176. rtt = (end_hrtime-start_hrtime) / 1000.0f;
  177. min_rtt = MIN(rtt, min_rtt);
  178. max_rtt = MAX(rtt, max_rtt);
  179. total_rtt += rtt;
  180. printd("%d bytes from %s: icmp_seq=%u ttl=%u time=%.1f ms\n", icmp_len, ip, seq, ipheader->ttl, rtt);
  181. fflush(stdout);
  182. ++ok_cnt;
  183. if (cnt > 0) sleep(1); // sleep a while, then agian
  184. }
  185. end_tick = gettick();
  186. printd("--- %s ping statistics ---\n", host);
  187. printd("%d packets transmitted, %d received, %d%% packet loss, time %d ms\n",
  188. send_cnt, recv_cnt, (send_cnt-recv_cnt)*100/(send_cnt==0?1:send_cnt), end_tick-start_tick);
  189. printd("rtt min/avg/max = %.3f/%.3f/%.3f ms\n",
  190. min_rtt, total_rtt/(ok_cnt==0?1:ok_cnt), max_rtt);
  191. closesocket(sockfd);
  192. return ok_cnt;
  193. error:
  194. closesocket(sockfd);
  195. return sockerrno == 0 ? -1 : -sockerrno;
  196. }