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

php如何开发API接口

8.1 ??????API

怎样使用百度api去开发一个php项目

Surface Pro 3 Windows 8.1 的蓝牙 API

java的api在哪里?