Chinaunix首页 | 论坛 | 博客
  • 博客访问: 116844
  • 博文数量: 26
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 122
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-11 10:02
个人简介

分享是一种快乐!

文章分类

全部博文(26)

文章存档

2014年(11)

2013年(15)

我的朋友

分类: C/C++

2013-07-21 23:50:03

1,实现的基本功能: 
客户端:发送一行文本给服务器,服务器显示收到的字节数,并返回收到的内容给客户端。 

2,一个单进程的实现实例: 
file echo.c: 
C代码  收藏代码
  1. #include "csapp.h"  
  2.   
  3. void echo(int connfd)   
  4. {  
  5.     size_t n;   
  6.     char buf[MAXLINE];   
  7.     rio_t rio;  
  8.   
  9.     Rio_readinitb(&rio, connfd);  
  10.     while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)   
  11.     { //line:netp:echo:eof  
  12.         printf("server received %d bytes\n", n);  
  13.         Rio_writen(connfd, buf, n);  
  14.     }  
  15. }  

file echoclient.c: 
C代码  收藏代码
  1. #include "csapp.h"  
  2.   
  3. int main(int argc, char **argv)   
  4. {  
  5.     int clientfd, port;  
  6.     char *host, buf[MAXLINE];  
  7.     rio_t rio;  
  8.   
  9.     if (argc != 3)   
  10.     {  
  11.         fprintf(stderr, "usage: %s  \n", argv[0]);  
  12.         exit(0);  
  13.     }  
  14.     host = argv[1];  
  15.     port = atoi(argv[2]);  
  16.   
  17.     clientfd = Open_clientfd(host, port);  
  18.     Rio_readinitb(&rio, clientfd);  
  19.   
  20.     while (Fgets(buf, MAXLINE, stdin) != NULL)   
  21.     {  
  22.         Rio_writen(clientfd, buf, strlen(buf));  
  23.         Rio_readlineb(&rio, buf, MAXLINE);  
  24.         Fputs(buf, stdout);  
  25.     }  
  26.     Close(clientfd); //line:netp:echoclient:close  
  27.     exit(0);  
  28. }  

file: echoserver.c 
C代码  收藏代码
  1. #include "csapp.h"  
  2.   
  3. void echo(int connfd);  
  4.   
  5. int main(int argc, char **argv)   
  6. {  
  7.     int listenfd, connfd, port, clientlen;  
  8.     struct sockaddr_in clientaddr;  
  9.     struct hostent *hp;  
  10.     char *haddrp;  
  11.     if (argc != 2)   
  12.     {  
  13.         fprintf(stderr, "usage: %s \n", argv[0]);  
  14.         exit(0);  
  15.     }  
  16.     port = atoi(argv[1]);  
  17.   
  18.     listenfd = Open_listenfd(port);  
  19.     while (1)   
  20.     {  
  21.         clientlen = sizeof(clientaddr);  
  22.         connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);  
  23.   
  24.         /* determine the domain name and IP address of the client */  
  25.         hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,   
  26.                 sizeof(clientaddr.sin_addr.s_addr), AF_INET);  
  27.         haddrp = inet_ntoa(clientaddr.sin_addr);  
  28.         printf("server connected to %s (%s)\n", hp->h_name, haddrp);  
  29.   
  30.         echo(connfd);  
  31.         Close(connfd);  
  32.     }  
  33.     exit(0);  
  34. }  

注:此时,只能一次处理一个客户端,当第二个客户端加入时,被阻塞。 
使用: 
服务端: 
./echoserver 8080 
server connected to kmplayer (127.0.0.1) 
server received 16 bytes 
server received 14 bytes 
server received 5 bytes 
客户端: 
./echoserver 8080 
server connected to kmplayer (127.0.0.1) 
server received 16 bytes 
server received 14 bytes 
server received 5 bytes 

3, 
基于进程的并发echo服务器: 
C代码  收藏代码
  1. #include "csapp.h"  
  2.   
  3. void echo(int connfd);  
  4.   
  5. void sigchld_handler(int sig)  
  6. {  
  7.     while (waitpid(-1, 0, WNOHANG) > 0) //准备好接受多个僵死进程  
  8.         ;  
  9.     return;  
  10. }  
  11.   
  12. int main(int argc, char **argv)  
  13. {  
  14.     int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);  
  15.     struct sockaddr_in clientaddr;  
  16.     struct hostent *hp;  
  17.     char *haddrp;  
  18.     if (argc != 2)  
  19.     {  
  20.         fprintf(stderr, "usage: %s \n", argv[0]);  
  21.         exit(0);  
  22.     }  
  23.     port = atoi(argv[1]);  
  24.     Signal(SIGCHLD, sigchld_handler); //回收子进程  
  25.   
  26.     listenfd = Open_listenfd(port);  
  27.     while (1)  
  28.     {  
  29.         connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);  
  30.   
  31.         if (Fork() == 0)  
  32.         {  
  33.             Close(listenfd); //关闭复制过来的监听描述符  
  34.             hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,  
  35.                 sizeof(clientaddr.sin_addr.s_addr), AF_INET);  
  36.             haddrp = inet_ntoa(clientaddr.sin_addr);  
  37.             printf("server connected to %s (%s)\n", hp->h_name, haddrp);  
  38.             echo(connfd);  
  39.             Close(connfd);  
  40.             exit(0);  
  41.         }  
  42.   
  43.         Close(connfd);  
  44.     }  
  45.     exit(0);  
  46. }  

4,基于I/O多路复用,实现了标准输入和客户端的echo服务器 
C代码  收藏代码
  1. /* 
  2.  * echoserveri.c - An iterative echo server 
  3.  */  
  4. /* $begin echoserverimain */  
  5. #include "csapp.h"  
  6.   
  7. void echo(int connfd);  
  8.   
  9. void command(void);  
  10.   
  11. int main(int argc, char **argv)  
  12. {  
  13.     int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);  
  14.     struct sockaddr_in clientaddr;  
  15.     struct hostent *hp;  
  16.     char *haddrp;  
  17.     fd_set read_set,ready_set;  
  18.     if (argc != 2)  
  19.     {  
  20.         fprintf(stderr, "usage: %s \n", argv[0]);  
  21.         exit(0);  
  22.     }  
  23.     port = atoi(argv[1]);  
  24.     listenfd = Open_listenfd(port);  
  25.   
  26.     FD_ZERO(&read_set);  
  27.     FD_SET(STDIN_FILENO, &read_set);  
  28.     FD_SET(listenfd, &read_set);  
  29.     while (1)  
  30.     {  
  31.         ready_set=read_set;//重新载入  
  32.         Select(listenfd+1, &ready_set, NULL, NULL, NULL); //挂起进程  
  33.   
  34.         if(FD_ISSET(STDIN_FILENO, &ready_set))  
  35.         {  
  36.             printf("server connected to local stdin.\n");  
  37.             command();  
  38.         }  
  39.         if(FD_ISSET(listenfd, &ready_set))  
  40.         {  
  41.             connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);  
  42.             hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,  
  43.             sizeof(clientaddr.sin_addr.s_addr), AF_INET);  
  44.             haddrp = inet_ntoa(clientaddr.sin_addr);  
  45.             printf("server connected to %s (%s)\n", hp->h_name, haddrp);  
  46.             echo(connfd);  
  47.             Close(connfd);  
  48.         }  
  49.     }  
  50.     exit(0);  
  51. }  
  52.   
  53. void command(void)  
  54. {  
  55.     char buf[MAXLINE];  
  56.     if (!Fgets(buf, MAXLINE, stdin))  
  57.         exit(0);  
  58.     printf("%s", buf);  
  59. }  

注:一旦服务器连接到一个客户端,就会连续回送输入行,直到客户端关闭。 
期间,你键入命令到标准输入将不会得到响应。 

5,基于I/O多路复用,实现了多个客户端的echo服务器 
C代码  收藏代码
  1. /* 
  2.  * echoserveri.c - An iterative echo server 
  3.  */  
  4. /* $begin echoserverimain */  
  5. #include "csapp.h"  
  6.   
  7. typedef struct  
  8. {  
  9.     int maxfd;  
  10.     fd_set read_set;  
  11.     fd_set ready_set;  
  12.     int nready;  
  13.     int maxi;  
  14.     int clientfd[FD_SETSIZE];  
  15.     rio_t clientrio[FD_SETSIZE];  
  16. }pool;  
  17.   
  18. void init_pool(int listenfd, pool* p);  
  19. void add_client(int connfd, pool* p);  
  20. void check_clients(pool* p);  
  21.   
  22. int byte_cnt=0;  
  23.   
  24. int main(int argc, char **argv)  
  25. {  
  26.     int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);  
  27.     struct sockaddr_in clientaddr;  
  28.     struct hostent *hp;  
  29.     static pool pool;  
  30.     char *haddrp;  
  31.     fd_set read_set,ready_set;  
  32.     if (argc != 2)  
  33.     {  
  34.         fprintf(stderr, "usage: %s \n", argv[0]);  
  35.         exit(0);  
  36.     }  
  37.     port = atoi(argv[1]);  
  38.     listenfd = Open_listenfd(port);  
  39.     init_pool(listenfd, &pool);  
  40.   
  41.     while (1)  
  42.     {  
  43.         pool.ready_set = pool.read_set;//重新载入  
  44.         pool.nready = Select(pool.maxfd+1, &pool.ready_set, NULL, NULL, NULL);  
  45.         if(FD_ISSET(listenfd, &pool.ready_set))  
  46.         {  
  47.             connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);  
  48.   
  49.             hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,  
  50.                                 sizeof(clientaddr.sin_addr.s_addr), AF_INET);  
  51.             haddrp = inet_ntoa(clientaddr.sin_addr);  
  52.             printf("server connected to %s (%s)\n", hp->h_name, haddrp);  
  53.             add_client(connfd, &pool);  
  54.         }  
  55.         check_clients(&pool);  
  56.     }  
  57.     exit(0);  
  58. }  
  59. void init_pool(int listenfd, pool* p)  
  60. {  
  61.     int i;  
  62.     p->maxi = -1;  
  63.     for(i=0; i
  64.         p->clientfd[i] = -1;  
  65.     p->maxfd = listenfd;  
  66.     FD_ZERO(&p->read_set);  
  67.     FD_SET(listenfd, &p->read_set);  
  68. }  
  69. void add_client(int connfd, pool* p)  
  70. {  
  71.     int i;  
  72.     p->nready--;  
  73.     for (i = 0; i < FD_SETSIZE; i++)  
  74.         if (p->clientfd[i] < 0)  
  75.         {  
  76.             p->clientfd[i] = connfd;  
  77.             Rio_readinitb(&p->clientrio[i], connfd);  
  78.             FD_SET(connfd, &p->read_set);  
  79.             if (connfd > p->maxfd)  
  80.                 p->maxfd = connfd;  
  81.             if (i > p->maxi)  
  82.                 p->maxi = i;  
  83.             break;  
  84.         }  
  85.         if(i == FD_SETSIZE)  
  86.             app_error("add_client error: Too many clients");  
  87. }  
  88. void check_clients(pool* p)  
  89. {  
  90.     int i, connfd, n;  
  91.     char buf[MAXLINE];  
  92.     rio_t rio;  
  93.     for (i = 0; (i <= p->maxi) && (p->nready > 0); i++)  
  94.     {  
  95.         connfd = p->clientfd[i];  
  96.         rio = p->clientrio[i];  
  97.         if ((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)  
  98.         {  
  99.             byte_cnt +=n;  
  100.             printf("Server received %d (%d total) bytes on fd %d\n",  
  101.                     n, byte_cnt, connfd);  
  102.             Rio_writen(connfd, buf, n);  
  103.         }  
  104.         else  
  105.         {  
  106.             Close(connfd);  
  107.             FD_CLR(connfd, &p->read_set);  
  108.             p->clientfd[i] = -1;  
  109.         }  
  110.     }  
  111. }  

注:客户端只有交替输入时,才可以正确处理 


6,基于多线程,实现了多个客户端的echo服务器 
C代码  收藏代码
  1. /* 
  2.  * echoserveri.c - An iterative echo server 
  3.  */  
  4. /* $begin echoserverimain */  
  5. #include "csapp.h"  
  6.   
  7. void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp,  
  8.             void * (*routine)(void *), void *argp)  
  9. {  
  10.     int rc;  
  11.   
  12.     if ((rc = pthread_create(tidp, attrp, routine, argp)) != 0)  
  13.     posix_error(rc, "Pthread_create error");  
  14. }  
  15.   
  16. //常常配合pthread_self,来终止当前线程  
  17. void Pthread_cancel(pthread_t tid)  
  18. {  
  19.     int rc;  
  20.   
  21.     if ((rc = pthread_cancel(tid)) != 0)  
  22.         posix_error(rc, "Pthread_cancel error");  
  23. }  
  24.   
  25. //阻塞, 直到等到指定的线程终止。  
  26. void Pthread_join(pthread_t tid, void **thread_return)  
  27. {  
  28.     int rc;  
  29.   
  30.     if ((rc = pthread_join(tid, thread_return)) != 0)  
  31.         posix_error(rc, "Pthread_join error");  
  32. }  
  33.   
  34. //线程默认是可结合的:意味着可以被其他线程收回资源和杀死  
  35. //该函数使线程变为分离的,那么 其资源就不必显式的回收了。  
  36. void Pthread_detach(pthread_t tid)  
  37. {  
  38.     int rc;  
  39.     if ((rc = pthread_detach(tid)) != 0)  
  40.         posix_error(rc, "Pthread_detach error");  
  41. }  
  42.   
  43. //线程显示终止  
  44. //主线程调用,等待所有其他线程终止,然后终止主线程和整个进程  
  45. void Pthread_exit(void *retval)  
  46. {  
  47.     pthread_exit(retval);  
  48. }  
  49. //某个 线程调用exit,会终止进程以及所有与该进程相关的线程  
  50.   
  51. //返回自己线程的ID  
  52. pthread_t Pthread_self(void)  
  53. {  
  54.     return pthread_self();  
  55. }  
  56.   
  57. void Pthread_once(pthread_once_t *once_control, void (*init_function)()) {  
  58.     pthread_once(once_control, init_function);  
  59. }  
  60.   
  61.   
  62. void echo(int connfd);  
  63. voidthread(void* vargp);  
  64.   
  65. int main(int argc, char **argv)  
  66. {  
  67.     int listenfd, *connfdp, port, clientlen = sizeof(struct sockaddr_in);  
  68.     struct sockaddr_in clientaddr;  
  69.     pthread_t tid;  
  70.     if (argc != 2)  
  71.     {  
  72.         fprintf(stderr, "usage: %s \n", argv[0]);  
  73.         exit(0);  
  74.     }  
  75.     port = atoi(argv[1]);  
  76.     listenfd = Open_listenfd(port);  
  77.   
  78.     while (1)  
  79.     {  
  80.         connfdp = (int*)Malloc(sizeof(int));  
  81.         *connfdp =Accept(listenfd, (SA *)&clientaddr, &clientlen);  
  82.         Pthread_create(&tid, NULL, thread, connfdp);  
  83.     }  
  84.     exit(0);  
  85. }  
  86.   
  87. voidthread(void* vargp)  
  88. {  
  89.     int connfd = *((int*)vargp);  
  90.     printf("server connected to a client.\n");     
  91.     Free(vargp);  
  92.     echo(connfd);  
  93.     Close(connfd);  
  94.     return NULL;  
  95. }  

阅读(3421) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~