代码开源(3)——UNIX中CS简单实现

   主要摘自《深入理解计算机系统》一书,略作整理,加了些备注。可以简单了解一下UNIX网络编程。下面程序已在Ubuntu9.10下测试通过。

     客户端主程序:

view plain print ?
  1. #include "client.h"  
  2.   
  3. int main(int argc, char **argv)  
  4. {  
  5.     int clientfd; //客户端套接字描述符  
  6.     if(argc != 3) //参数必须是3个  
  7.     {  
  8.         fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);  
  9.         return 0;  
  10.     }  
  11.   
  12.     clientfd = open_clientfd(argv[1], atoi(argv[2]));  //打开连接  
  13.     exchange_data(clientfd);                           //与服务器交换数据  
  14.     close(clientfd);                                   //关闭客户端套接字  
  15.     return 0;  
  16. }  

      主要包含了一个头文件,里面有具体的实现。下面是client.h的定义。其中所用的IO输入输出,就是代码开源(2)——UNIX 健壮I/O函数介绍的健壮IO,包含进来即可。这里就不重复给出了。

      对客户端程序,当标准输入遇到EOF,或者因为用户在键盘键入ctrl-d,或者因为在一个重定向的输入文件中用尽了所有的文本行,循环就结束。这时客户端关闭套接字描述符,并发送一个EOF通知服务器,服务器就可以收到一个为0的返回码,从而确定客户端已经关闭。也就是说,当服务器和客户端都运行时,客户端要想退出,最简单的就是按ctrl-d。

view plain print ?
  1. #ifndef CLIENT_H_  
  2. #define CLIENT_H_  
  3.   
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <sys/socket.h>  
  7. #include <string.h>  
  8. #include <netdb.h>  
  9. #include "rio.h"  
  10.   
  11. #define MAX_LINE 1024  
  12.   
  13. void unix_error(char *msg);                    //错误处理程序  
  14. void exchange_data(int fd);                    //与服务器交换数据  
  15. int open_clientfd(char *hostname, int port);   //打开连接,外部调用  
  16. int _open_clientfd(char *hostname, int port);  //打开连接,内部调用  
  17.   
  18. //错误处理程序,打印错误信息  
  19. void unix_error(char *msg)  
  20. {  
  21.     fprintf(stderr, "%s: %s\n", msg, strerror(errno));  
  22.     exit(0);  
  23. }  
  24. //与服务器交换数据  
  25. void exchange_data(int fd)  
  26. {  
  27.     char buf[MAX_LINE];  
  28.     rio_t rio;  
  29.     rio_readinitb(&rio, fd); //初始化rio_t  
  30.   
  31.     while(fgets(buf, MAX_LINE, stdin) != NULL)   //从标准输入读入数据  
  32.     {  
  33.         rio_writen(fd, buf, strlen(buf));       //发往服务器端  
  34.         rio_readlineb(&rio, buf, MAX_LINE);     //从服务器读一行  
  35.         fputs(buf, stdout);                     //写到标准输出  
  36.     }  
  37. }  
  38. //打开连接,外部调用  
  39. int open_clientfd(char *hostname, int port)  
  40. {  
  41.     int rc;  
  42.     if((rc = _open_clientfd(hostname, port)) < 0)  
  43.     {  
  44.         if(rc == -1)  
  45.             unix_error("Open client UNIX error");  
  46.         else  
  47.             unix_error("Open client DNS error");  
  48.     }  
  49.     return rc;  
  50. }  
  51. //打开连接,内部调用  
  52. int _open_clientfd(char *hostname, int port)  
  53. {  
  54.     int clientfd;                  //客户端套接字描述符  
  55.     struct hostent *hp;            //主机条目  
  56.     struct sockaddr_in serveraddr; //服务器地址  
  57.   
  58.     if((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //返回-1表示出错  
  59.         return -1;  
  60.     if((hp = gethostbyname(hostname)) == NULL)      //返回空表示出错  
  61.         return -2;  
  62.   
  63.     bzero((char *)&serveraddr, sizeof(serveraddr)); //清零  
  64.     serveraddr.sin_family = AF_INET;  
  65.     bcopy((char *)hp->h_addr, (char *)&serveraddr.sin_addr.s_addr, hp->h_length); //服务器IP地址  
  66.     serveraddr.sin_port = htons(port); //转换为网络序  
  67.   
  68.     if(connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) //连接  
  69.         return -1;  
  70.     printf("Connect to server success\n");  
  71.     return clientfd;  //连接成功  
  72. }  
  73.   
  74. #endif /* CLIENT_H_ */  

         下面给出服务器的主程序:

view plain print ?
  1. #include "server.h"  
  2.   
  3. int main(int argc, char **argv)  
  4. {  
  5.     int listenfd, connfd;  
  6.     unsigned int clientlen;         //地址长度  
  7.     struct sockaddr_in clientaddr;  //客户端地址  
  8.     struct hostent *hp;  
  9.     char *haddrp;                   //客户端域名  
  10.   
  11.     if(argc != 2)                   //参数必须是2个  
  12.     {  
  13.         fprintf(stderr, "usage: %s <port>\n",argv[0]);  
  14.         return 0;  
  15.     }  
  16.     listenfd = open_listenfd(atoi(argv[1])); //进入监听状态  
  17.     clientlen = sizeof(clientaddr);  
  18.     while(1) //只支持单个连接  
  19.     {  
  20.         if((connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen)) < 0) //建立连接  
  21.         {  
  22.             fprintf(stderr, "Accept error");  
  23.             exit(0);  
  24.         }  
  25.   
  26.         hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,  
  27.                 sizeof(clientaddr.sin_addr.s_addr), AF_INET);  //客户端域名  
  28.         haddrp = inet_ntoa(clientaddr.sin_addr);               //客户端IP地址  
  29.   
  30.         printf("%s (%s) connect to server\n", hp->h_name, haddrp);  
  31.         exchange_data(connfd);   //与客户端交换数据  
  32.         close(connfd);           //客户端断开连接  
  33.         printf("%s (%s) close\n", hp->h_name, haddrp);  
  34.     }  
  35.     return 0;  
  36. }  

           主要包含了一个头文件,里面有具体的实现。下面是server.h的定义。

view plain print ?
  1. #ifndef SERVER_H_  
  2. #define SERVER_H_  
  3.   
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <sys/socket.h>  
  7. #include <string.h>  
  8. #include <netdb.h>  
  9. #include <arpa/inet.h>  
  10. #include <errno.h>  
  11. #include <unistd.h>  
  12. #include "rio.h"  
  13.   
  14. #define MAX_LINE 1024  
  15. #define MAX_LISTEN 1024          //最大连接数  
  16.   
  17. void unix_error(char *msg);      //错误处理  
  18. void exchange_data(int connfd);  //与客户端交换数据  
  19. int open_listenfd(int port);     //打开监听,外部调用  
  20. int _open_listenfd(int port);    //打开监听,内部调用  
  21.   
  22. //错误处理程序,打印错误信息  
  23. void unix_error(char *msg)  
  24. {  
  25.     fprintf(stderr, "%s: %s\n", msg, strerror(errno));  
  26.     exit(0);  
  27. }  
  28. //与客户端交换数据  
  29. void exchange_data(int connfd)  
  30. {  
  31.     size_t n;  
  32.     char buf[MAX_LINE];  
  33.     rio_t rio;  
  34.     rio_readinitb(&rio, connfd);  
  35.     while((n = rio_readlineb(&rio, buf, MAX_LINE)) != 0) //读到末尾结束  
  36.     {  
  37.         printf("server received %d bytes\n", n); //收到的字节数  
  38.         rio_writen(connfd, buf, n);              //反显到客户端  
  39.     }  
  40. }  
  41. //打开监听  
  42. int open_listenfd(int port)  
  43. {  
  44.     int rc;  
  45.     if((rc = _open_listenfd(port)) < 0)  
  46.         unix_error("Open server listen error");  
  47.     return rc;  
  48. }  
  49. //建立监听函数,内部调用  
  50. int _open_listenfd(int port)  
  51. {  
  52.     int listenfd;   //服务器套接字描述符  
  53.     int optval = 1;  
  54.     struct sockaddr_in serveraddr;  
  55.   
  56.     if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //返回-1表示出错  
  57.         return -1;  
  58.   
  59.     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,  
  60.             (const void *)&optval, sizeof(int)) < 0)  //忽略地址正在使用的错误  
  61.     {  
  62.         close(listenfd);  
  63.         return -1;  
  64.     }  
  65.   
  66.     bzero((char *)&serveraddr, sizeof(serveraddr));  
  67.     serveraddr.sin_family = AF_INET;  
  68.     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);    //接受来自主机的任何IP地址  
  69.     serveraddr.sin_port = htons((unsigned short)port); //网络字节顺序  
  70.   
  71.     if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) //绑定  
  72.     {  
  73.         close(listenfd);  
  74.         return -1;  
  75.     }  
  76.     if(listen(listenfd, MAX_LISTEN) < 0) //开始监听  
  77.     {  
  78.         close(listenfd);  
  79.         return -1;  
  80.     }  
  81.     return listenfd;  
  82. }  
  83. #endif /* SERVER_H_ */  
       上面代码中,用到了一些结构体及函数,这里给出一个简单的介绍。

view plain print ?
  1. /* <netdb.h> 
  2.  * DNS主机条目结构 
  3.  *  struct hostent{ 
  4.  *      char *h_name;        官方名字 
  5.  *      char **h_aliases;    一组别名 
  6.  *      int h_addrtype;      地址类型 
  7.  *      int h_length;        地址字节长度 
  8.  *      char **h_addr_list;  一组IP地址 
  9.  * }; 
  10.  */  
  11.   
  12. /* <sys/socket.h> 
  13.  * 套接字地址结构 
  14.  * struct sockaddr{ 
  15.  *      unsigned short sa_family; 
  16.  *      sa_data[14]; 
  17.  * }; 
  18.  * struct in_addr{ 
  19.  *      unsigned int s_addr;  网络字节顺序,大端法 
  20.  * }; 
  21.  * struct sockaddr_in{ 后缀_in表示internet 
  22.  *      unsigned short sin_family; 
  23.  *      unsigned short sin_port;    端口号 
  24.  *      struct in_addr sin_addr;    IP地址 
  25.  *      unsigned char sin_zero[8];  补齐 
  26.  * }; 
  27.  */  
  28. /* 
  29.  * <string.h> 
  30.  * extern void bzero(void *s, int n) 
  31.  * extern void bcopy(const void *src, void *dest, int n) 
  32.  */  

你可能感兴趣的:(socket,unix,Stream,网络,服务器,Exchange)