8.1 服务器开发 API 函数封装,select 优化服务器和客户端
Posted 天道酬勤
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8.1 服务器开发 API 函数封装,select 优化服务器和客户端相关的知识,希望对你有一定的参考价值。
1 #include <unistd.h> 2 #include <sys/types.h> 3 #include <sys/socket.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <signal.h> 7 #include <sys/wait.h> 8 9 #include <fcntl.h> 10 11 12 #include <stdlib.h> 13 #include <stdio.h> 14 #include <errno.h> 15 #include <string.h> 16 17 #include "commsocket.h" 18 19 typedef struct _SckHandle 20 { 21 int sockArray[100]; 22 int arrayNum; 23 int sockfd; 24 int contime; 25 int sendtime; 26 int revtime; 27 28 }SckHandle; 29 30 /** 31 * readn - 读取固定字节数 32 * @fd: 文件描述符 33 * @buf: 接收缓冲区 34 * @count: 要读取的字节数 35 * 成功返回count,失败返回-1,读到EOF返回<count 36 */ 37 ssize_t readn(int fd, void *buf, size_t count) 38 { 39 size_t nleft = count; 40 ssize_t nread; 41 char *bufp = (char*)buf; 42 43 while (nleft > 0) 44 { 45 if ((nread = read(fd, bufp, nleft)) < 0) 46 { 47 if (errno == EINTR) 48 continue; 49 return -1; 50 } 51 else if (nread == 0) 52 return count - nleft; 53 54 bufp += nread; 55 nleft -= nread; 56 } 57 58 return count; 59 } 60 61 /** 62 * writen - 发送固定字节数 63 * @fd: 文件描述符 64 * @buf: 发送缓冲区 65 * @count: 要读取的字节数 66 * 成功返回count,失败返回-1 67 */ 68 ssize_t writen(int fd, const void *buf, size_t count) 69 { 70 size_t nleft = count; 71 ssize_t nwritten; 72 char *bufp = (char*)buf; 73 74 while (nleft > 0) 75 { 76 if ((nwritten = write(fd, bufp, nleft)) < 0) 77 { 78 if (errno == EINTR) 79 continue; 80 return -1; 81 } 82 else if (nwritten == 0) 83 continue; 84 85 bufp += nwritten; 86 nleft -= nwritten; 87 } 88 89 return count; 90 } 91 92 /** 93 * recv_peek - 仅仅查看套接字缓冲区数据,但不移除数据 94 * @sockfd: 套接字 95 * @buf: 接收缓冲区 96 * @len: 长度 97 * 成功返回>=0,失败返回-1 98 */ 99 ssize_t recv_peek(int sockfd, void *buf, size_t len) 100 { 101 while (1) 102 { 103 int ret = recv(sockfd, buf, len, MSG_PEEK); 104 if (ret == -1 && errno == EINTR) 105 continue; 106 return ret; 107 } 108 } 109 110 111 //函数声明 112 //客户端环境初始化 113 int sckCliet_init(void **handle, int contime, int sendtime, int revtime, int nConNum) 114 { 115 int ret = 0; 116 if (handle == NULL ||contime<0 || sendtime<0 || revtime<0) 117 { 118 ret = Sck_ErrParam; 119 printf("func sckCliet_init() err: %d, check (handle == NULL ||contime<0 || sendtime<0 || revtime<0) ", ret); 120 return ret; 121 } 122 123 SckHandle *tmp = (SckHandle *)malloc(sizeof(SckHandle)); 124 if (tmp == NULL) 125 { 126 ret = Sck_ErrMalloc; 127 printf("func sckCliet_init() err: malloc %d ", ret); 128 return ret; 129 } 130 131 tmp->contime = contime; 132 tmp->sendtime = sendtime; 133 tmp->revtime = revtime; 134 tmp->arrayNum = nConNum; 135 136 137 /* 138 int sockfd; 139 int i = 0; 140 for (i=0; i<1; i++) 141 { 142 //链表的顺序 143 sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 144 if (sockfd < 0) 145 { 146 ret = errno; 147 printf("func socket() err: %d ", ret); 148 return ret; 149 } 150 tmp->sockfd = sockfd; 151 } 152 */ 153 154 *handle = tmp; 155 return ret; 156 } 157 158 /** 159 * activate_noblock - 设置I/O为非阻塞模式 160 * @fd: 文件描符符 161 */ 162 int activate_nonblock(int fd) 163 { 164 int ret = 0; 165 int flags = fcntl(fd, F_GETFL); 166 if (flags == -1) 167 { 168 ret = flags; 169 printf("func activate_nonblock() err:%d", ret); 170 return ret; 171 } 172 173 174 flags |= O_NONBLOCK; 175 ret = fcntl(fd, F_SETFL, flags); 176 if (ret == -1) 177 { 178 printf("func activate_nonblock() err:%d", ret); 179 return ret; 180 } 181 return ret; 182 } 183 184 /** 185 * deactivate_nonblock - 设置I/O为阻塞模式 186 * @fd: 文件描符符 187 */ 188 int deactivate_nonblock(int fd) 189 { 190 int ret = 0; 191 int flags = fcntl(fd, F_GETFL); 192 if (flags == -1) 193 { 194 ret = flags; 195 printf("func deactivate_nonblock() err:%d", ret); 196 return ret; 197 } 198 199 flags &= ~O_NONBLOCK; 200 ret = fcntl(fd, F_SETFL, flags); 201 if (ret == -1) 202 { 203 printf("func deactivate_nonblock() err:%d", ret); 204 return ret; 205 } 206 return ret; 207 } 208 209 210 211 /** 212 * connect_timeout - connect 213 * @fd: 套接字 214 * @addr: 要连接的对方地址 215 * @wait_seconds: 等待超时秒数,如果为0表示正常模式 216 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT 217 */ 218 static int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) 219 { 220 int ret; 221 socklen_t addrlen = sizeof(struct sockaddr_in); 222 223 if (wait_seconds > 0) 224 activate_nonblock(fd); 225 226 ret = connect(fd, (struct sockaddr*)addr, addrlen); 227 if (ret < 0 && errno == EINPROGRESS) 228 { 229 //printf("11111111111111111111 "); 230 fd_set connect_fdset; 231 struct timeval timeout; 232 FD_ZERO(&connect_fdset); 233 FD_SET(fd, &connect_fdset); 234 timeout.tv_sec = wait_seconds; 235 timeout.tv_usec = 0; 236 do 237 { 238 // 一但连接建立,则套接字就可写 所以connect_fdset放在了写集合中 239 ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout); 240 } while (ret < 0 && errno == EINTR); 241 if (ret == 0) 242 { 243 ret = -1; 244 errno = ETIMEDOUT; 245 } 246 else if (ret < 0) 247 return -1; 248 else if (ret == 1) 249 { 250 //printf("22222222222222222 "); 251 /* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/ 252 /* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */ 253 int err; 254 socklen_t socklen = sizeof(err); 255 int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen); 256 if (sockoptret == -1) 257 { 258 return -1; 259 } 260 if (err == 0) 261 { 262 //printf("3333333333333 "); 263 ret = 0; 264 } 265 else 266 { 267 //printf("4444444444444444:%d ", err); 268 errno = err; 269 ret = -1; 270 } 271 } 272 } 273 if (wait_seconds > 0) 274 { 275 deactivate_nonblock(fd); 276 } 277 return ret; 278 } 279 280 281 282 // 283 int sckCliet_getconn(void *handle, char *ip, int port, int *connfd) 284 { 285 286 int ret = 0; 287 SckHandle *tmp = NULL; 288 if (handle == NULL || ip==NULL || connfd==NULL || port<0 || port>65537) 289 { 290 ret = Sck_ErrParam; 291 printf("func sckCliet_getconn() err: %d, check (handle == NULL || ip==NULL || connfd==NULL || port<0 || port>65537) ", ret); 292 return ret; 293 } 294 295 // 296 int sockfd; 297 sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 298 if (sockfd < 0) 299 { 300 ret = errno; 301 printf("func socket() err: %d ", ret); 302 return ret; 303 } 304 305 struct sockaddr_in servaddr; 306 memset(&servaddr, 0, sizeof(servaddr)); 307 servaddr.sin_family = AF_INET; 308 servaddr.sin_port = htons(port); 309 servaddr.sin_addr.s_addr = inet_addr(ip); 310 311 tmp = (SckHandle* )handle; 312 313 /* 314 ret = connect(sockfd, (struct sockaddr*) (&servaddr), sizeof(servaddr)); 315 if (ret < 0) 316 { 317 ret = errno; 318 printf("func connect() err: %d ", ret); 319 return ret; 320 } 321 */ 322 323 ret = connect_timeout(sockfd, (struct sockaddr_in*) (&servaddr), (unsigned int )tmp->contime); 324 if (ret < 0) 325 { 326 if (ret==-1 && errno == ETIMEDOUT) 327 { 328 ret = Sck_ErrTimeOut; 329 return ret; 330 } 331 else 332 { 333 printf("func connect_timeout() err: %d ", ret); 334 } 335 } 336 337 *connfd = sockfd; 338 339 return ret; 340 341 } 342 343 344 345 /** 346 * write_timeout - 写超时检测函数,不含写操作 347 * @fd: 文件描述符 348 * @wait_seconds: 等待超时秒数,如果为0表示不检测超时 349 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT 350 */ 351 int write_timeout(int fd, unsigned int wait_seconds) 352 { 353 int ret = 0; 354 if (wait_seconds > 0) 355 { 356 fd_set write_fdset; 357 struct timeval timeout; 358 359 FD_ZERO(&write_fdset); 360 FD_SET(fd, &write_fdset); 361 362 timeout.tv_sec = wait_seconds; 363 timeout.tv_usec = 0; 364 do 365 { 366 ret = select(fd + 1, NULL, &write_fdset, NULL, &timeout); 367 } while (ret < 0 && errno == EINTR); 368 369 if (ret == 0) 370 { 371 ret = -1; 372 errno = ETIMEDOUT; 373 } 374 else if (ret == 1) 375 ret = 0; 376 } 377 378 return ret; 379 } 380 381 382 //客户端发送报文 383 int sckClient_send(void *handle, int connfd, unsigned char *data, int datalen) 384 { 385 int ret = 0; 386 387 SckHandle *tmp = NULL; 388 tmp = (SckHandle *)handle; 389 ret = write_timeout(connfd, tmp->sendtime); 390 if (ret == 0) 391 { 392 int writed = 0; 393 unsigned char *netdata = ( unsigned char *)malloc(datalen + 4); 394 if ( netdata == NULL) 395 { 396 ret = Sck_ErrMalloc; 397 printf("func sckClient_send() mlloc Err:%d ", ret); 398 return ret; 399 } 400 int netlen = htonl(datalen); 401 memcpy(netdata, &netlen, 4); 402 memcpy(netdata+4, data, datalen); 403 404 writed = writen(connfd, netdata, datalen + 4); 405 if (writed < (datalen + 4) ) 406 { 407 if (netdata != NULL) 408 { 409 free(netdata); 410 netdata = NULL; 411 } 412 return writed; 413 } 414 415 } 416 417 if (ret < 0) 418 { 419 //失败返回-1,超时返回-1并且errno = ETIMEDOUT 420 if (ret == -1 && errno == ETIMEDOUT) 421 { 422 ret = Sck_ErrTimeOut; 423 printf("func sckClient_send() mlloc Err:%d ", ret); 424 return ret; 425 } 426 return ret; 427 } 428 429 return ret; 430 } 431 432 433 434 /** 435 * read_timeout - 读超时检测函数,不含读操作 436 * @fd: 文件描述符 437 * @wait_seconds: 等待超时秒数,如果为0表示不检测超时 438 * 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT 439 */ 440 int read_timeout(int fd, unsigned int wait_seconds) 441 { 442 int ret = 0; 443 if (wait_seconds > 0) 444 { 445 fd_set read_fdset; 446 struct timeval timeout; 447 448 FD_ZERO(&read_fdset); 449 FD_SET(fd, &read_fdset); 450 451 timeout.tv_sec = wait_seconds; 452 timeout.tv_usec = 0; 453 454 //select返回值三态 455 //1 若timeout时间到(超时),没有检测到读事件 ret返回=0 456 //2 若ret返回<0 && errno == EINTR 说明select的过程中被别的信号中断(可中断睡眠原理) 457 //2-1 若返回-1,select出错 458 //3 若ret返回值>0 表示有read事件发生,返回事件发生的个数 459 460 do 461 { 462 ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout); 463 } while (ret < 0 && errno == EINTR); 464 465 if (ret == 0) 466 { 467 ret = -1; 468 errno = ETIMEDOUT; 469 } 470 else if (ret == 1) 471 ret = 0; 472 } 473 474 return ret; 475 } 476 477 //客户端端接受报文 478 int sckClient_rev(void *handle, int connfd, unsigned char *out, int *outlen) 479 { 480 481 int ret = 0; 482 SckHandle *tmpHandle = (SckHandle *)handle; 483 484 if (handle==NULL || out==NULL) 485 { 486 ret = Sck_ErrParam; 487 printf("func sckClient_rev() timeout , err:%d ", Sck_ErrTimeOut); 488 return ret; 489 } 490 491 ret = read_timeout(connfd, tmpHandle->revtime ); //bugs modify bombing 492 if (ret != 0) 493 { 494 if (ret==-1 || errno == ETIMEDOUT) 495 { 496 ret = Sck_ErrTimeOut; 497 printf("func sckClient_rev() timeout , err:%d ", Sck_ErrTimeOut); 498 return ret; 499 } 500 else 501 { 502 printf("func sckClient_rev() timeout , err:%d ", Sck_ErrTimeOut); 503 return ret; 504 } 505 } 506 507 int netdatalen = 0; 508 ret = readn(connfd, &netdatalen, 4); //读包头 4个字节 509 if (ret == -1) 510 { 511 printf("func readn() err:%d ", ret); 512 return ret; 513 } 514 else if (ret < 4) 515 { 516 ret = Sck_ErrPeerClosed; 517 printf("func readn() err peer closed:%d ", ret); 518 return ret; 519 } 520 521 int n; 522 n = ntohl(netdatalen); 523 ret = readn(connfd, out, n); //根据长度读数据 524 if (ret == -1) 525 { 526 printf("func readn() err:%d ", ret); 527 return ret; 528 } 529 else if (ret < n) 530 { 531 ret = Sck_ErrPeerClosed; 532 printf("func readn() err peer closed:%d ", ret); 533 return ret; 534 } 535 536 *outlen = n; 537 538 return 0; 539 } 540 541 // 客户端环境释放 542 int sckClient_destroy(void *handle) 543 { 544 if (handle != NULL) 545 { 546 free(handle); 547 } 548 return 0; 549 } 550 551 int sckCliet_closeconn(int connfd) 552 { 553 if (connfd >=0 ) 554 { 555 close(connfd); 556 } 557 return 0; 558 } 559 560 561 562 ///////////////////////////////////////////////////////////////////////////////////// 563 //函数声明 564 //服务器端初始化 565 int sckServer_init(int port, int *listenfd) 566 { 567 int ret = 0; 568 int mylistenfd; 569 struct sockaddr_in servaddr; 570 memset(&servaddr, 0, sizeof(servaddr)); 571 servaddr.sin_family = AF_INET; 572 servaddr.sin_port = htons(port); 573 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 574 575 576 mylistenfd = socket(PF_INET, SOCK_STREAM, 0); 577 if (mylistenfd < 0) 578 { 579 ret = errno ; 580 printf("func socket() err:%d ", ret); 581 return ret; 582 } 583 584 585 int on = 1; 586 ret = setsockopt(mylistenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ); 587 if (ret < 0) 588 { 589 ret = errno ; 590 printf("func setsockopt() err:%d ", ret); 591 return ret; 592 } 593 594 595 ret = bind(mylistenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); 596 if (ret < 0) 597 { 598 ret = errno ; 599 printf("func bind() err:%d ", ret); 600 return ret; 601 } 602 603 ret = listen(mylistenfd, SOMAXCONN); 604 if (ret < 0) 605 { 606 ret = errno ; 607 printf("func listen() err:%d ", ret); 608 return ret; 609 } 610 611 *listenfd = mylistenfd; 612 613 return 0; 614 } 615 616 /** 617 * accept_timeout - 带超时的accept 618 * @fd: 套接字 619 * @addr: 输出参数,返回对方地址 620 * @wait_seconds: 等待超时秒数,如果为0表示正常模式 621 * 成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT 622 */ 623 int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds) 624 { 625 int ret; 626 socklen_t addrlen = sizeof(struct sockaddr_in); 627 628 if (wait_seconds > 0) 629 { 630 fd_set accept_fdset; 631 struct timeval timeout; 632 FD_ZERO(&accept_fdset); 633 FD_SET(fd, &accept_fdset); 634 timeout.tv_sec = wait_seconds; 635 timeout.tv_usec = 0; 636 do 637 { 638 ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout); 639 } while (ret < 0 && errno == EINTR); 640 if (ret == -1) 641 return -1; 642 else if (ret == 0) 643 { 644 errno = ETIMEDOUT; 645 return -1; 646 } 647 } 648 649 //一但检测出 有select事件发生,表示对等方完成了三次握手,客户端有新连接建立 650 //此时再调用accept将不会堵塞 651 if (addr != NULL) 652 ret = accept(fd, (struct sockaddr*)addr, &addrlen); //返回已连接套接字 653 else 654 ret = accept(fd, NULL, NULL); 655 if (ret == -1) 656 { 657 ret = errno; 658 printf("func accept() err:%d ", ret); 659 return ret; 660 } 661 662 663 return ret; 664 } 665 666 int sckServer_accept(int listenfd, int *connfd, int timeout) 667 { 668 int ret = 0; 669 670 ret = accept_timeout(listenfd, NULL, (unsigned int) timeout); 671 if (ret < 0) 672 { 673 if (ret == -1 && errno == ETIMEDOUT) 674 { 675 ret = Sck_ErrTimeOut; 676 printf("func accept_timeout() timeout err:%d ", ret); 677 return ret; 678 } 679 else 680 { 681 ret = errno; 682 printf("func accept_timeout() err:%d ", ret); 683 return ret; 684 } 685 } 686 687 *connfd = ret; 688 return 0; 689 } 690 //服务器端发送报文 691 int sckServer_send(int connfd, unsigned char *data, int datalen, int timeout) 692 { 693 int ret = 0; 694 695 ret = write_timeout(connfd, timeout); 696 if (ret == 0) 697 { 698 int writed = 0; 699 unsigned char *netdata = ( unsigned char *)malloc(datalen + 4); 700 if ( netdata == NULL) 701 { 702 ret = Sck_ErrMalloc; 703 printf("func sckServer_send() mlloc Err:%d ", ret); 704 return ret; 705 } 706 int netlen = htonl(datalen); 707 memcpy(netdata, &netlen, 4); 708 memcpy(netdata+4, data, datalen); 709 710 writed = writen(connfd, netdata, datalen + 4); 711 if (writed < (datalen + 4) ) 712 { 713 if (netdata != NULL) 714 { 715 free(netdata); 716 netdata = NULL; 717 } 718 return writed; 719 } 720 721 } 722 723 if (ret < 0) 724 { 725 //失败返回-1,超时返回-1并且errno = ETIMEDOUT 726 if (ret == -1 && errno == ETIMEDOUT) 727 { 728 ret = Sck_ErrTimeOut; 729 printf("func sckServer_send() mlloc Err:%d ", ret); 730 return ret; 731 } 732 return ret; 733 } 734 735 return ret; 736 } 737 //服务器端端接受报文 738 int sckServer_rev(int connfd, unsigned char *out, int *outlen, int timeout) 739 { 740 741 int ret = 0; 742 743 if (out==NULL || outlen==NULL) 744 { 745 ret = Sck_ErrParam; 746 printf("func sckClient_rev() timeout , err:%d ", Sck_ErrTimeOut); 747 return ret; 748 } 749 750 ret = read_timeout(connfd, timeout); //bugs modify bombing 751 if (ret != 0) 752 { 753 if (ret==-1 || errno == ETIMEDOUT) 754 { 755 ret = Sck_ErrTimeOut; 756 printf("func sckClient_rev() timeout , err:%d ", Sck_ErrTimeOut); 757 return ret; 758 } 759 else 760 { 761 printf("func sckClient_rev() timeout , err:%d ", Sck_ErrTimeOut); 762 return ret; 763 } 764 } 765 766 int netdatalen = 0; 767 ret = readn(connfd, &netdatalen, 4); //读包头 4个字节 768 if (ret == -1) 769 { 770 printf("func readn() err:%d ", ret); 771 return ret; 772 } 773 else if (ret < 4) 774 { 775 ret = Sck_ErrPeerClosed; 776 printf("func readn() err peer closed:%d ", ret); 777 return ret; 778 } 779 780 int n; 781 n = ntohl(netdatalen); 782 ret = readn(connfd, out, n); //根据长度读数据 783 if (ret == -1) 784 { 785 printf("func readn() err:%d ", ret); 786 return ret; 787 } 788 else if (ret < n) 789 { 790 ret = Sck_ErrPeerClosed; 791 printf("func readn() err peer closed:%d ", ret); 792 return ret; 793 } 794 795 *outlen = n; 796 797 return 0; 798 } 799 800 //服务器端环境释放 801 int sckServer_destroy(void *handle) 802 { 803 return 0; 804 }
select优化服务器:
1 #include <unistd.h> 2 #include <sys/types.h> 3 #include <sys/socket.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <signal.h> 7 #include <sys/wait.h> 8 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <errno.h> 12 #include <string.h> 13 14 #define ERR_EXIT(m) 15 do 16 { 17 perror(m); 18 exit(EXIT_FAILURE); 19 } while(0) 20 21 ssize_t readn(int fd, void *buf, size_t count) 22 { 23 size_t nleft = count; 24 ssize_t nread; 25 char *bufp = (char*)buf; 26 27 while (nleft > 0) 28 { 29 if ((nread = read(fd, bufp, nleft)) < 0) 30 { 31 if (errno == EINTR) 32 continue; 33 return -1; 34 } 35 else if (nread == 0) 36 return count - nleft; 37 38 bufp += nread; 39 nleft -= nread; 40 } 41 42 return count; 43 } 44 45 ssize_t writen(int fd, const void *buf, size_t count) 46 { 47 size_t nleft = count; 48 ssize_t nwritten; 49 char *bufp = (char*)buf; 50 51 while (nleft > 0) 52 { 53 if ((nwritten = write(fd, bufp, nleft)) < 0) 54 { 55 if (errno == EINTR) 56 continue; 57 return -1; 58 } 59 else if (nwritten == 0) 60 continue; 61 62 bufp += nwritten; 63 nleft -= nwritten; 64 } 65 66 return count; 67 } 68 69 ssize_t recv_peek(int sockfd, void *buf, size_t len) 70 { 71 while (1) 72 { 73 int ret = recv(sockfd, buf, len, MSG_PEEK); 74 if (ret == -1 && errno == EINTR) 75 continue; 76 return ret; 77 } 78 } 79 80 ssize_t readline(int sockfd, void *buf, size_t maxline) 81 { 82 int ret; 83 int nread; 84 char *bufp = buf; 85 int nleft = maxline; 86 while (1) 87 { 88 ret = recv_peek(sockfd, bufp, nleft); 89 if (ret < 0) 90 return ret; 91 else if (ret == 0) 92 return ret; 93 94 nread = ret; 95 int i; 96 for (i=0; i<nread; i++) 97 { 98 if (bufp[i] == ‘ ‘) 99 { 100 ret = readn(sockfd, bufp, i+1); 101 if (ret != i+1) 102 exit(EXIT_FAILURE); 103 104 return ret; 105 } 106 } 107 108 if (nread > nleft) 109 exit(EXIT_FAILURE); 110 111 nleft -= nread; 112 ret = readn(sockfd, bufp, nread); 113 if (ret != nread) 114 exit(EXIT_FAILURE); 115 116 bufp += nread; 117 } 118 119 return -1; 120 } 121 122 void echo_srv(int conn) 123 { 124 char recvbuf[1024]; 125 while (1) 126 { 127 memset(recvbuf, 0, sizeof(recvbuf)); 128 int ret = readline(conn, recvbuf, 1024); 129 if (ret == -1) 130 ERR_EXIT("readline"); 131 if (ret == 0) 132 { 133 printf("client close "); 134 break; 135 } 136 137 fputs(recvbuf, stdout); 138 writen(conn, recvbuf, strlen(recvbuf)); 139 } 140 } 141 142 void handle_sigchld(int sig) 143 { 144 /* wait(NULL);*/ 145 while (waitpid(-1, NULL, WNOHANG) > 0) 146 ; 147 } 148 149 void handle_sigpipe(int sig) 150 { 151 printf("recv a sig=%d ", sig); 152 } 153 154 int main(void) 155 { 156 /*signal(SIGPIPE, SIG_IGN);*/ 157 signal(SIGPIPE, handle_sigpipe); 158 /* signal(SIGCHLD, SIG_IGN);*/ 159 signal(SIGCHLD, handle_sigchld); 160 int listenfd; 161 if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 162 /* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/ 163 ERR_EXIT("socket"); 164 165 struct sockaddr_in servaddr; 166 memset(&servaddr, 0, sizeof(servaddr)); 167 servaddr.sin_family = AF_INET; 168 servaddr.sin_port = htons(5188); 169 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 170 /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/ 171 /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ 172 173 int on = 1; 174 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 175 ERR_EXIT("setsockopt"); 176 177 if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 178 ERR_EXIT("bind"); 179 if (listen(listenfd, SOMAXCONN) < 0) 180 ERR_EXIT("listen"); 181 182 struct sockaddr_in peeraddr; 183 socklen_t peerlen; 184 int conn; 185 186 /* 187 pid_t pid; 188 while (1) 189 { 190 if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) 191 ERR_EXIT("accept"); 192 193 printf("ip=%s port=%d ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 194 195 pid = fork(); 196 if (pid == -1) 197 ERR_EXIT("fork"); 198 if (pid == 0) 199 { 200 close(listenfd); 201 echo_srv(conn); 202 exit(EXIT_SUCCESS); 203 } 204 else 205 close(conn); 206 } 207 */ 208 209 int i; 210 int client[FD_SETSIZE]; //支持的最大客户端连接数 数组 211 int maxi = 0; //最大的不空闲的位置 212 213 printf("FD_SETSIZE:%d ", FD_SETSIZE); 214 for (i=0; i<FD_SETSIZE; i++) 215 client[i] = -1; 216 217 int nready; 218 int maxfd = listenfd; 219 fd_set rset; 220 fd_set allset; 221 222 FD_ZERO(&rset); 223 FD_ZERO(&allset); 224 FD_SET(listenfd, &allset); 225 printf("FD_SETSIZE:%d ", FD_SETSIZE); 226 while (1) 227 { 228 rset = allset; 229 //listenfd 可读,表示有客户端连接进来了,可以建立一个新的连接 230 nready = select(maxfd+1, &rset, NULL, NULL, NULL); 231 if (nready == -1) 232 { 233 if (errno == EINTR) 234 continue; 235 236 ERR_EXIT("select"); 237 } 238 if (nready == 0) 239 continue; 240 241 if (FD_ISSET(listenfd, &rset)) //若侦听套接口产生可读事件 说明3次握手已完成,有客户端已经连接建立 242 { 243 peerlen = sizeof(peeraddr); 244 conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); //处理accept 245 if (conn == -1) 246 ERR_EXIT("accept"); 247 248 for (i=0; i<FD_SETSIZE; i++) 249 { 250 if (client[i] < 0) 251 { 252 client[i] = conn; 253 if (i > maxi) 254 maxi = i; 255 break; 256 } 257 } 258 259 if (i == FD_SETSIZE) 260 { 261 fprintf(stderr, "too many clients "); 262 exit(EXIT_FAILURE); 263 } 264 printf("ip=%s port=%d ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 265 266 FD_SET(conn, &allset); //把新的连接 放入集合中 267 if (conn > maxfd) //同时按照条件,更新maxfd //维护最大描述符 268 maxfd = conn; 269 270 if (--nready <= 0) 271 continue; 272 273 } 274 275 for (i=0; i<=maxi; i++) //检测已连接的调节字conn是否可读 276 { 277 conn = client[i]; 278 if (conn == -1) 279 continue; 280 281 if (FD_ISSET(conn, &rset)) //当前连接是否可读 282 { 283 char recvbuf[1024] = {0}; 284 int ret = readline(conn, recvbuf, 1024); 285 if (ret == -1) 286 ERR_EXIT("readline"); 287 if (ret == 0) 288 { 289 printf("client close "); 290 FD_CLR(conn, &allset); //若对方已退出 从集合中清除 291 client[i] = -1; //保存连接的数组 也置成-1 //也可进一步控制 若i是把maxfd,需要把maxifd变成第二大maxifd 292 close(conn); 293 } 294 295 fputs(recvbuf, stdout); 296 //sleep(4); 297 writen(conn, recvbuf, strlen(recvbuf)); 298 299 if (--nready <= 0) 300 break; 301 } 302 } 303 } 304 return 0; 305 }
select优化客户端:
1 #include <unistd.h> 2 #include <sys/types.h> 3 #include <sys/socket.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <signal.h> 7 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include <errno.h> 11 #include <string.h> 12 13 #define ERR_EXIT(m) 14 do 15 { 16 perror(m); 17 exit(EXIT_FAILURE); 18 } while(0) 19 20 ssize_t readn(int fd, void *buf, size_t count) 21 { 22 size_t nleft = count; 23 ssize_t nread; 24 char *bufp = (char*)buf; 25 26 while (nleft > 0) 27 { 28 if ((nread = read(fd, bufp, nleft)) < 0) 29 { 30 if (errno == EINTR) 31 continue; 32 return -1; 33 } 34 else if (nread == 0) 35 return count - nleft; 36 37 bufp += nread; 38 nleft -= nread; 39 } 40 41 return count; 42 } 43 44 ssize_t writen(int fd, const void *buf, size_t count) 45 { 46 size_t nleft = count; 47 ssize_t nwritten; 48 char *bufp = (char*)buf; 49 50 while (nleft > 0) 51 { 52 if ((nwritten = write(fd, bufp, nleft)) < 0) 53 { 54 if (errno == EINTR) 55 continue; 56 return -1; 57 } 58 else if (nwritten == 0) 59 continue; 60 61 bufp += nwritten; 62 nleft -= nwritten; 63 } 64 65 return count; 66 } 67 68 ssize_t recv_peek(int sockfd, void *buf, size_t len) 69 { 70 while (1) 71 { 72 int ret = recv(sockfd, buf, len, MSG_PEEK); 73 if (ret == -1 && errno == EINTR) 74 continue; 75 return ret; 76 } 77 } 78 79 80 ssize_t readline(int sockfd, void *buf, size_t maxline) 81 { 82 int ret; 83 int nread; 84 char *bufp = buf; 85 int nleft = maxline; 86 while (1) 87 { 88 ret = recv_peek(sockfd, bufp, nleft); 89 if (ret < 0) 90 return ret; 91 else if (ret == 0) 92 return ret; 93 94 nread = ret; 95 int i; 96 for (i=0; i<nread; i++) 97 { 98 if (bufp[i] == ‘ ‘) 99 { 100 ret = readn(sockfd, bufp, i+1); 101 if (ret != i+1) 102 exit(EXIT_FAILURE); 103 104 return ret; 105 } 106 } 107 108 if (nread > nleft) 109 exit(EXIT_FAILURE); 110 111 nleft -= nread; 112 ret = readn(sockfd, bufp, nread); 113 if (ret != nread) 114 exit(EXIT_FAILURE); 115 116 bufp += nread; 117 } 118 119 return -1; 120 } 121 122 void echo_cli(int sock) 123 { 124 /* 125 char sendbuf[1024] = {0}; 126 char recvbuf[1024] = {0}; 127 while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) 128 { 129 writen(sock, sendbuf, strlen(sendbuf)); 130 131 int ret = readline(sock, recvbuf, sizeof(recvbuf)); 132 if (ret == -1) 133 ERR_EXIT("readline"); 134 else if (ret == 0) 135 { 136 printf("client close "); 137 break; 138 } 139 140 fputs(recvbuf, stdout); 141 memset(sendbuf, 0, sizeof(sendbuf)); 142 memset(recvbuf, 0, sizeof(recvbuf)); 143 } 144 145 close(sock); 146 */ 147 fd_set rset; 148 FD_ZERO(&rset); 149 150 int nready; 151 int maxfd; 152 int fd_stdin = fileno(stdin); 153 154 if (fd_stdin > sock) 155 maxfd = fd_stdin; 156 else 157 maxfd = sock; 158 159 char sendbuf[1024] = {0}; 160 char recvbuf[1024] = {0}; 161 162 while (1) 163 { 164 FD_SET(fd_stdin, &rset); 165 FD_SET(sock, &rset); 166 nready = select(maxfd+1, &rset, NULL, NULL, NULL); 167 if (nready == -1) 168 ERR_EXIT("select"); 169 170 if (nready == 0) 171 continue; 172 173 if (FD_ISSET(sock, &rset)) 174 { 175 int ret = readline(sock, recvbuf, sizeof(recvbuf)); 176 if (ret == -1) 177 ERR_EXIT("readline"); 178 else if (ret == 0) 179 { 180 printf("server close "); 181 break; 182 } 183 184 fputs(recvbuf, stdout); 185 memset(recvbuf, 0, sizeof(recvbuf)); 186 } 187 if (FD_ISSET(fd_stdin, &rset)) 188 { 189 if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) 190 break; 191 writen(sock, sendbuf, strlen(sendbuf)); 192 memset(sendbuf, 0, sizeof(sendbuf)); 193 } 194 } 195 196 close(sock); 197 } 198 199 void handle_sigpipe(int sig) 200 { 201 printf("recv a sig=%d ", sig); 202 } 203 204 int main(void) 205 { 206 /* 207 signal(SIGPIPE, handle_sigpipe); 208 */ 209 signal(SIGPIPE, SIG_IGN); 210 int sock; 211 if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 212 ERR_EXIT("socket"); 213 214 struct sockaddr_in servaddr; 215 memset(&servaddr, 0, sizeof(servaddr)); 216 servaddr.sin_family = AF_INET; 217 servaddr.sin_port = htons(5188); 218 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 219 220 if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 221 ERR_EXIT("connect"); 222 223 struct sockaddr_in localaddr; 224 socklen_t addrlen = sizeof(localaddr); 225 if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0) 226 ERR_EXIT("getsockname"); 227 228 printf("ip=%s port=%d ", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); 229 230 231 echo_cli(sock); 232 233 return 0; 234 }
以上是关于8.1 服务器开发 API 函数封装,select 优化服务器和客户端的主要内容,如果未能解决你的问题,请参考以下文章
使用 GraphQL 封装 REST API / 仅使用 GraphQL