hsocket.c 7.7 KB

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