unix套接字

socket,涉及到的概念比较多,就不详细展开了。从编程角度简单的说,为了在客户端和服务器之间建立一个连接,服务器端需要依次调用这些函数:

  1. socket,获得一个socket
  2. bind,将获得的socket绑定到指定的地址和端口
  3. listen,让此socket进入等待状态,等待客户端的连接
  4. accept,当建立起与客户端之间的连接时,此函数返回代表此连接的socket。即类似于文件描述符,我们可以把客户端看作文件,向网络上进行读写操作,从而完成通信过程。
客户端需要调用的函数:
  1. socket
  2. connect,向指定地址的服务器的指定端口发起连接
服务器和客户端都需用到的通信函数:
  1. send
  2. recv
详细信息和函数方法等就不赘述了,请参见man 文档。

下面来看几道练习。
1. 从客户端把用户输入的信息发送到服务器,直到用户输入“over”。这是一道基础题,直接上代码了。
server:
[cpp]  view plain copy
  1. #include <arpa/inet.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <netinet/in.h>  
  7. #include <errno.h>  
  8. #include <string.h>  
  9. #include <unistd.h>  
  10.   
  11. int main(int argc, char *argv[])  
  12. {  
  13.     int numPort, lenMsg, i;  
  14.     int sockServ, sockClient;  
  15.     struct sockaddr_in adrServ, adrClient;  
  16.     char message[256];  
  17.     char * overMsg = "over";  
  18.     int adrSize = sizeof(adrClient);  
  19.     int yes = 1;  
  20.   
  21.     if(argc < 2)  
  22.     {  
  23.         printf("syntaxe : %s numPort\n", argv[0]);  
  24.         return -1;  
  25.     }  
  26.       
  27.   
  28.     //set local addr and port  
  29.     numPort = atoi(argv[1]);  
  30.     adrServ.sin_addr.s_addr = INADDR_ANY;  
  31.     adrServ.sin_family = AF_INET;  
  32.     adrServ.sin_port = htons(numPort);  
  33.     bzero(&(adrServ.sin_zero), 8) ;  
  34.       
  35.     //Get a socket  
  36.     if((sockServ = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("sockServet"); return -1; }  
  37.       
  38.     //We can set some param here.// paramètrage de la socket pour réutiliser le N° ce port sans délai  
  39.     if(setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); return -1; }  
  40.   
  41.     // bind  
  42.     if(bind(sockServ, (struct sockaddr *)(&adrServ), sizeof(adrServ)) == -1) { perror("bind"); return -1; }  
  43.   
  44.     // place la socket en écoute  
  45.     if(listen(sockServ, 1) == -1) { perror("listen"); return -1; }  
  46.       
  47.   
  48.     // wait for a connection from a client  
  49.     if((sockClient = accept(sockServ, (struct sockaddr *)(&adrClient), &adrSize)) == -1) { perror("accept"); return -1; }  
  50.     printf("New socket client : %s\n", inet_ntoa(adrClient.sin_addr));  
  51.     do  
  52.     {  
  53.         // Read a msg from client  
  54.         if((lenMsg = recv(sockClient, message, 256, 0)) == -1)  
  55.         { perror("recv"); return -1; }  
  56.         message[lenMsg] = '\0';  
  57.         printf("New msg received : %s\n", message);  
  58.         //printf("New msg received : %s, %d\n", message, strcmp(message, overMsg));  
  59.           
  60.     }while(strcmp(message, overMsg) != 0);    
  61.     // fermeture de la connexion  
  62.     close(sockServ);  
  63.       
  64.     return 0;  
  65. }  


client:
[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<netinet/in.h>  
  4. #include<sys/types.h>  
  5. #include<unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<string.h>  
  8. void main(int argc, char* argv[])  
  9. {  
  10.     if(argc < 3)  
  11.     {  
  12.         printf("Syntax:%s server_addr <portNum>\n", argv[0]);  
  13.         exit(0);  
  14.     }  
  15.   
  16.   
  17.     int fdSock;  
  18.     //get a socket  
  19.     if( (fdSock=socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket"); exit(1);}  
  20.       
  21.     //config the address of the server to connect  
  22.     struct sockaddr_in addrServer;  
  23.     addrServer.sin_family = AF_INET;//Address family  
  24.     inet_pton(AF_INET, argv[1], &(addrServer.sin_addr));//Convert the addr from "presentation" to "numeric"  
  25.     addrServer.sin_port=htons(atoi(argv[2]));//set the port number  
  26.     //Connect to server  
  27.     if( connect( fdSock, (struct sockaddr *)(&addrServer), sizeof(addrServer))== -1){perror("connect"); exit(1);}  
  28.       
  29.     char buf[1024];  
  30.     gets(buf);//get a msg from terminal  
  31.     while(buf[0] != '\0')  
  32.     {  
  33.         printf("Gets gets : %s\n", buf);  
  34.         //Send the msg to server. See also "sendto" and "sendmsg"  
  35.         if( send(fdSock, buf, strlen(buf), 0) == -1){perror("Send"); exit(1);}  
  36.         if(strcmp(buf, "over") == 0)  
  37.         {  
  38.             printf("Exit...\n");  
  39.             close(fdSock);  
  40.             exit(0);  
  41.         }  
  42.         gets(buf);  
  43.     }  
  44.   
  45.   
  46. }  
运行结果:
[plain]  view plain copy
  1. client:  
[plain]  view plain copy
  1. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./client12 127.0.0.1 9999  
  2. hello  
  3. Gets gets : hello  
  4. byebye  
  5. Gets gets : byebye  
  6. over  
  7. Gets gets : over  
  8. Exit...  
  9. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$   
[plain]  view plain copy
  1. server:  
  2. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./server12 9999  
  3. New socket client : 127.0.0.1  
  4. New msg received : hello  
  5. New msg received : byebye  
  6. New msg received : over  
  7. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$   




2. 这次结合上exec,从客户端发送命令到服务器,服务器在本地执行命令。
server:
[cpp]  view plain copy
  1. #include <arpa/inet.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <netinet/in.h>  
  7. #include <errno.h>  
  8. #include <string.h>  
  9. #include <unistd.h>  
  10.   
  11. #define MAX_NUM_ARG 10  
  12. void exeCmd(char * cmd);  
  13.   
  14. int main(int argc, char *argv[])  
  15. {  
  16.     int numPort, lenMsg, i;  
  17.     int sockServ, sockClient;  
  18.     struct sockaddr_in adrServ, adrClient;  
  19.     char message[256];  
  20.     char * overMsg = "over";  
  21.     int adrSize = sizeof(adrClient);  
  22.     int yes = 1;  
  23.   
  24.     if(argc < 2)  
  25.     {  
  26.         printf("syntaxe : %s numPort\n", argv[0]);  
  27.         return -1;  
  28.     }  
  29.       
  30.   
  31.     //set local addr and port  
  32.     numPort = atoi(argv[1]);  
  33.     adrServ.sin_addr.s_addr = INADDR_ANY;  
  34.     adrServ.sin_family = AF_INET;  
  35.     adrServ.sin_port = htons(numPort);  
  36.     bzero(&(adrServ.sin_zero), 8) ;  
  37.       
  38.     //Get a socket  
  39.     if((sockServ = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("sockServet"); return -1; }  
  40.       
  41.     //We can set some param here.// paramètrage de la socket pour réutiliser le N° ce port sans délai  
  42.     if(setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); return -1; }  
  43.   
  44.     // bind  
  45.     if(bind(sockServ, (struct sockaddr *)(&adrServ), sizeof(adrServ)) == -1) { perror("bind"); return -1; }  
  46.   
  47.     // place la socket en écoute  
  48.     if(listen(sockServ, 1) == -1) { perror("listen"); return -1; }  
  49.       
  50.   
  51.     // wait for a connection from a client  
  52.     if((sockClient = accept(sockServ, (struct sockaddr *)(&adrClient), &adrSize)) == -1) { perror("accept"); return -1; }  
  53.     printf("New socket client : %s\n", inet_ntoa(adrClient.sin_addr));  
  54.     do  
  55.     {  
  56.         // Read a cmd from client  
  57.         if((lenMsg = recv(sockClient, message, 256, 0)) == -1)  
  58.             { perror("recv"); return -1; }  
  59.         message[lenMsg] = '\0';  
  60.         printf("\nNew cmd received : %s, result:\n", message);  
  61.         exeCmd(message);  
  62.         //printf("New msg received : %s, %d\n", message, strcmp(message, overMsg));  
  63.           
  64.     }while(strcmp(message, overMsg) != 0);    
  65.     // fermeture de la connexion  
  66.     close(sockServ);  
  67.       
  68.     return 0;  
  69. }  
  70.   
  71.   
  72. void exeCmd(char * cmd)  
  73. {  
  74.     if(fork()==0)  
  75.     {  
  76.         char *argv[MAX_NUM_ARG] = {NULL};  
  77.         int i;  
  78.         char *arg;  
  79.         for(i=0; i<MAX_NUM_ARG; i++)  
  80.         {  
  81.             if(i>0) cmd = NULL;  
  82.             arg = strtok(cmd, " ");  
  83.             if(arg != NULL)  
  84.             {  
  85.                 //put this arg into argv[]  
  86.                 argv[i] = calloc( sizeof(char), strlen(arg+1)) ;  
  87.                 strcpy(argv[i], arg);  
  88.             }  
  89.             else  
  90.             {//execute this cmd  
  91.                 execvp(argv[0], argv);  
  92.                 exit(0);  
  93.             }  
  94.               
  95.         }  
  96.         exit(0);  
  97.     }  
  98. }  

client:
[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<netinet/in.h>  
  4. #include<sys/types.h>  
  5. #include<unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<string.h>  
  8. void main(int argc, char* argv[])  
  9. {  
  10.     if(argc < 3)  
  11.     {  
  12.         printf("Syntax:%s server_addr <portNum>\n", argv[0]);  
  13.         exit(0);  
  14.     }  
  15.   
  16.     int fdSock;  
  17.     //get a socket  
  18.     if( (fdSock=socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket"); exit(1);}  
  19.       
  20.     //config the address of the server to connect  
  21.     struct sockaddr_in addrServer;  
  22.     addrServer.sin_family = AF_INET;//Address family  
  23.     inet_pton(AF_INET, argv[1], &(addrServer.sin_addr));//Convert the addr from "presentation" to "numeric"  
  24.     addrServer.sin_port=htons(atoi(argv[2]));//set the port number  
  25.     //Connect to server  
  26.     if( connect( fdSock, (struct sockaddr *)(&addrServer), sizeof(addrServer))== -1){perror("connect"); exit(1);}  
  27.       
  28.     char buf[1024];  
  29.     printf("\nexcute_cmd_in_distance$");  
  30.     gets(buf);//get a cmd from terminal  
  31.     while(buf[0] != '\0')  
  32.     {  
  33.         //printf("Gets gets : %s\n", buf);  
  34.         //Send the msg to server. See also "sendto" and "sendmsg"  
  35.         if( send(fdSock, buf, strlen(buf), 0) == -1){perror("Send"); exit(1);}  
  36.         if(strcmp(buf, "over") == 0)  
  37.         {  
  38.             printf("Exit...\n");  
  39.             close(fdSock);  
  40.             exit(0);  
  41.         }  
  42.         printf("\nexcute_cmd_in_distance$");  
  43.         gets(buf);  
  44.     }  
  45.   
  46. }  

运行结果:
client:
[plain]  view plain copy
  1. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./client3 127.0.0.1 9999  
  2.   
  3. excute_cmd_in_distance$ls  
  4.   
  5. excute_cmd_in_distance$ls -l  
  6.   
  7. excute_cmd_in_distance$pwd  
  8.   
  9. excute_cmd_in_distance$over  
  10. Exit...  
  11. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$   

server:
[plain]  view plain copy
  1. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./server3 9999  
  2. New socket client : 127.0.0.1  
  3.   
  4. New cmd received : ls, result:  
  5. 12_client.c  3_client.c  client   client12  in.h      server3    td6-1_serveur-modif.c  types.h  
  6. 12_server.c  3_server.c  client1  client3   server12  socket.h    td6.pdf  
  7.   
  8. New cmd received : ls -l, result:  
  9. total 152  
  10. -rw-r--r-- 1 administrateur administrateur  1171 déc.  26 12:39 12_client.c  
  11. -rw-r--r-- 1 administrateur administrateur  1842 déc.  26 12:36 12_server.c  
  12. -rw-r--r-- 1 administrateur administrateur  1250 déc.  26 13:28 3_client.c  
  13. -rw-r--r-- 1 administrateur administrateur  2323 déc.  26 13:28 3_server.c  
  14. -rw-r--r-- 1 administrateur administrateur  7563 déc.   5 15:17 client  
  15. -rwxrwxr-x 1 administrateur administrateur  7549 déc.  26 11:47 client1  
  16. -rwxrwxr-x 1 administrateur administrateur  7623 déc.  26 12:31 client12  
  17. -rwxrwxr-x 1 administrateur administrateur  7622 déc.  26 13:28 client3  
  18. -rw-r--r-- 1 administrateur administrateur 18637 janv. 22  2011 in.h  
  19. -rwxrwxr-x 1 administrateur administrateur  7668 déc.  26 12:36 server12  
  20. -rwxrwxr-x 1 administrateur administrateur  7914 déc.  26 13:28 server3  
  21. -rw-r--r-- 1 administrateur administrateur  9597 janv. 22  2011 socket.h  
  22. -rw-r--r-- 1 administrateur administrateur  1953 déc.  26 11:46 td6-1_serveur-modif.c  
  23. -rw-r--r-- 1 administrateur administrateur 44356 nov.  30 15:39 td6.pdf  
  24. -rw-r--r-- 1 administrateur administrateur  6838 janv. 22  2011 types.h  
  25.   
  26. New cmd received : pwd, result:  
  27. /home/administrateur/Bureau/POLYTECH/unix/TD UNIX2012/td6  
  28.   
  29. New cmd received : over, result:  
  30. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$   



3. 理解”一切皆文件“的概念。将server的标准输入输出重定向到client,即从client读取命令字符串,并将执行结果返回给client输出。到这里不难发现我们所要做的有点像个远程终端。
并无特殊,加上重定向(在执行命令的子进程中)即可。
server:
[cpp]  view plain copy
  1. #include <arpa/inet.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <netinet/in.h>  
  7. #include <errno.h>  
  8. #include <string.h>  
  9. #include <unistd.h>  
  10.   
  11. #define MAX_NUM_ARG 10  
  12. void exeCmd(int sockClient, char * cmd)  
  13. {  
  14.       
  15.     if(fork()==0)  
  16.     {  
  17.         //Set redirection  
  18.         dup2(sockClient, 0);  
  19.         dup2(sockClient, 1);  
  20.         close(sockClient);  
  21.   
  22.         char *argv[MAX_NUM_ARG] = {NULL};  
  23.         int i;  
  24.         char *arg;  
  25.         for(i=0; i<MAX_NUM_ARG; i++)  
  26.         {  
  27.             if(i>0) cmd = NULL;  
  28.             arg = strtok(cmd, " ");  
  29.             if(arg != NULL)  
  30.             {  
  31.                 //put this arg into argv[]  
  32.                 //argv[i] = calloc( sizeof(char), strlen(arg+1)) ;  
  33.                 //strcpy(argv[i], arg);  
  34.                 argv[i] = arg;//We can also simply do like this.  
  35.                 fprintf(stderr, "cmd[%d] = %s\n",i,arg);  
  36.             }  
  37.             else  
  38.             {//execute this cmd  
  39.                 execvp(argv[0], argv);  
  40.                 exit(0);  
  41.             }  
  42.               
  43.         }  
  44.         exit(0);  
  45.     }  
  46. }  
  47.   
  48. int main(int argc, char *argv[])  
  49. {  
  50.       
  51.   
  52.     int numPort, lenMsg, i;  
  53.     int sockServ, sockClient;  
  54.     struct sockaddr_in adrServ, adrClient;  
  55.     char message[256];  
  56.     char * overMsg = "over";  
  57.     int adrSize = sizeof(adrClient);  
  58.     int yes = 1;  
  59.   
  60.     if(argc < 2)  
  61.     {  
  62.         printf("syntaxe : %s numPort\n", argv[0]);  
  63.         return -1;  
  64.     }  
  65.       
  66.   
  67.     //set local addr and port  
  68.     numPort = atoi(argv[1]);  
  69.     adrServ.sin_addr.s_addr = INADDR_ANY;  
  70.     adrServ.sin_family = AF_INET;  
  71.     adrServ.sin_port = htons(numPort);  
  72.     bzero(&(adrServ.sin_zero), 8) ;  
  73.       
  74.     //Get a socket  
  75.     if((sockServ = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("sockServet"); return -1; }  
  76.       
  77.     //We can set some param here.// paramètrage de la socket pour réutiliser le N° ce port sans délai  
  78.     if(setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); return -1; }  
  79.   
  80.     // bind  
  81.     if(bind(sockServ, (struct sockaddr *)(&adrServ), sizeof(adrServ)) == -1) { perror("bind"); return -1; }  
  82.   
  83.     // place la socket en écoute  
  84.     if(listen(sockServ, 1) == -1) { perror("listen"); return -1; }  
  85.       
  86.   
  87.     // wait for a connection from a client  
  88.     if((sockClient = accept(sockServ, (struct sockaddr *)(&adrClient), &adrSize)) == -1) { perror("accept"); return -1; }  
  89.     printf("New socket client : %s\n", inet_ntoa(adrClient.sin_addr));  
  90.   
  91.     do  
  92.     {  
  93.         // Read a cmd from client  
  94.         //fgets(stdin, message);  
  95.         //fscanf(sockClient,"%s",message);  
  96.         if((lenMsg = recv(sockClient, message, 256, 0)) == -1)  
  97.             { perror("recv"); return -1; }  
  98.         else if (lenMsg == 0)  
  99.         {  
  100.             close(sockClient);  
  101.             return 0;  
  102.         }  
  103.         message[lenMsg] = '\0';  
  104.         printf("New cmd received : %s\n", message);  
  105.         exeCmd(sockClient, message);  
  106.         //fflush(sockClient);  
  107.         //printf("New msg received : %s, %d\n", message, strcmp(message, overMsg));  
  108.           
  109.     }while(strcmp(message, overMsg) != 0);    
  110.     // fermeture de la connexion  
  111.     close(sockClient);  
  112.       
  113.     return 0;  
  114. }  
server运行结果:
[plain]  view plain copy
  1. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./server4 9999  
  2. New socket client : 127.0.0.1  
  3. New cmd received : ls  
  4. cmd[0] = ls  
  5. New cmd received : pwd  
  6. cmd[0] = pwd  
  7. New cmd received : date  
  8. cmd[0] = date  
  9. New cmd received : ls -l  
  10. cmd[0] = ls  
  11. cmd[1] = -l  
  12. New cmd received : over  
  13. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ cmd[0] = over  

client:
[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<netinet/in.h>  
  4. #include<sys/types.h>  
  5. #include<unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<string.h>  
  8. void main(int argc, char* argv[])  
  9. {  
  10.     if(argc < 3)  
  11.     {  
  12.         printf("Syntax:%s server_addr <portNum>\n", argv[0]);  
  13.         exit(0);  
  14.     }  
  15.   
  16.     int fdSock, res;  
  17.     //get a socket  
  18.     if( (fdSock=socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket"); exit(1);}  
  19.       
  20.     //config the address of the server to connect  
  21.     struct sockaddr_in addrServer;  
  22.     addrServer.sin_family = AF_INET;//Address family  
  23.     inet_pton(AF_INET, argv[1], &(addrServer.sin_addr));//Convert the addr from "presentation" to "numeric"  
  24.     addrServer.sin_port=htons(atoi(argv[2]));//set the port number  
  25.     //Connect to server  
  26.     if( connect( fdSock, (struct sockaddr *)(&addrServer), sizeof(addrServer))== -1){perror("connect"); exit(1);}  
  27.       
  28.     char buf[1024];  
  29.     printf("\nexcute_cmd_in_distance$");  
  30.     gets(buf);//get a cmd from terminal  
  31.     while(buf[0] != '\0')  
  32.     {  
  33.         //printf("Gets gets : %s\n", buf);  
  34.         //Send the msg to server. See also "sendto" and "sendmsg"  
  35.         if( send(fdSock, buf, strlen(buf), 0) == -1){perror("Send"); exit(1);}  
  36.         if(strcmp(buf, "over") == 0)  
  37.         {  
  38.             printf("Exit...\n");  
  39.             close(fdSock);  
  40.             exit(0);  
  41.         }  
  42.         if( (res = recv(fdSock, buf, 1024, 0)) == -1){perror("recv"); exit(1);}  
  43.         else if(res == 0) {close(fdSock); exit(0);}  
  44.         else  
  45.         {  
  46.             buf[res] = '\0';  
  47.             printf("\n%s",buf);  
  48.         }  
  49.         printf("\nexcute_cmd_in_distance$");  
  50.         gets(buf);  
  51.     }  
  52.   
  53. }  

client运行结果:
[plain]  view plain copy
  1. /home/administrateur/Bureau/POLYTECH/unix/TD UNIX2012/td6  
  2.   
  3. excute_cmd_in_distance$date  
  4.   
  5. mercredi 26 décembre 2012, 17:10:46 (UTC+0100)  
  6.   
  7. excute_cmd_in_distance$ls -l  
  8.   
  9. total 88  
  10. -rw-r--r-- 1 administrateur administrateur  1171 déc.  26 12:39 12_client.c  
  11. -rw-r--r-- 1 administrateur administrateur  1842 déc.  26 12:36 12_server.c  
  12. -rw-r--r-- 1 administrateur administrateur  1250 déc.  26 13:28 3_client.c  
  13. -rw-r--r-- 1 administrateur administrateur  2323 déc.  26 13:28 3_server.c  
  14. -rw-r--r-- 1 administrateur administrateur  1433 déc.  26 17:10 4_client.c  
  15. -rw-r--r-- 1 administrateur administrateur  2626 déc.  26 17:06 4_server.c  
  16. -rwxrwxr-x 1 administrateur administrateur  7658 déc.  26 17:10 client4  
  17. drwxrwxr-x 2 administrateur administrateur  4096 déc.  26 14:54 doc  
  18. -rwxrwxr-x 1 administrateur administrateur  7947 déc.  26 17:10 server4  
  19. -rw-r--r-- 1 administrateur administrateur 44356 nov.  30 15:39 td6.pdf  
  20.   
  21. excute_cmd_in_distance$over  
  22. Exit...  
  23. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$   

5. 在第4题的基础上,我们考虑I/O多路复用。试想,当client发送完命令,并且正在等待执行结果的时候,用户又在终端输入了一条命令。由于程序被recv阻塞,用户输入的命令并不会被处理。有没有一种方法可以监听所有的io操作,并在任何一个操作结束阻塞的时候通知我们呢?没错,我们在谈select和poll函数。下面的实现使用了poll函数,请注意看它是如何同时监听所有io并在任何一个io操作准备就绪时返回的。
client:
[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<netinet/in.h>  
  4. #include<sys/types.h>  
  5. #include<unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<string.h>  
  8. #include<poll.h>  
  9. void main(int argc, char* argv[])  
  10. {  
  11.     if(argc < 3)  
  12.     {  
  13.         printf("Syntax:%s server_addr <portNum>\n", argv[0]);  
  14.         exit(0);  
  15.     }  
  16.   
  17.     int fdSock, res, nready;  
  18.     //get a socket  
  19.     if( (fdSock=socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket"); exit(1);}  
  20.       
  21.     //config the address of the server to connect  
  22.     struct sockaddr_in addrServer;  
  23.     addrServer.sin_family = AF_INET;//Address family  
  24.     inet_pton(AF_INET, argv[1], &(addrServer.sin_addr));//Convert the addr from "presentation" to "numeric"  
  25.     addrServer.sin_port=htons(atoi(argv[2]));//set the port number  
  26.     //Connect to server  
  27.     if( connect( fdSock, (struct sockaddr *)(&addrServer), sizeof(addrServer))== -1){perror("connect"); exit(1);}  
  28.       
  29.     char buf[1024];  
  30.     printf("\nexcute_cmd_in_distance$");  
  31.     //gets(buf);//get a cmd from terminal  
  32.       
  33.     //POLL added here  
  34.     struct pollfd pfds[2];  
  35.     bzero(pfds, sizeof(pfds[0]));     
  36.     bzero(pfds+1, sizeof(pfds[0]));  
  37.     pfds[0].fd = 0;  
  38.     pfds[0].events = POLLRDNORM;//The event waited.(Normal read)  
  39.   
  40.     pfds[1].fd = fdSock;  
  41.     pfds[1].events = POLLRDNORM;  
  42.       
  43.     while(buf[0] != '\0')  
  44.     {  
  45.         printf("\nexcute_cmd_in_distance$");  
  46.         nready = poll(pfds, 2, -1);//nready is the number of ready fds  
  47.         if(pfds[0].revents & POLLRDNORM)  
  48.         {  
  49.             //deal with user input, send cmd to server  
  50.             gets(buf);  
  51.             if( send(fdSock, buf, strlen(buf), 0) == -1){perror("Send"); exit(1);}  
  52.             if(strcmp(buf, "over") == 0)  
  53.             {  
  54.                 printf("Exit...\n");  
  55.                 close(fdSock);  
  56.                 exit(0);  
  57.             }  
  58.               
  59.             pfds[0].revents = 0;  
  60.             nready --;  
  61.             if(nready == 0)  
  62.                 continue;  
  63.         }  
  64.         if(pfds[1].revents & POLLRDNORM )  
  65.         {  
  66.             //deal with socket, display the result of the last cmd  
  67.             pfds[1].revents = 0;  
  68.                   
  69.             if( (res = recv(fdSock, buf, 1024, 0)) == -1){perror("recv"); exit(1);}  
  70.             else if(res == 0) {close(fdSock); exit(0);}  
  71.             else  
  72.             {  
  73.                 buf[res] = '\0';  
  74.                 printf("\n%s",buf);  
  75.             }  
  76.         }  
  77.           
  78.     }  
  79.   
  80. }  

server:代码无太大变化,除了exeCmd函数。现在我们使用”bash“命令来执行其它命令。
[cpp]  view plain copy
  1. #include <arpa/inet.h>  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <netinet/in.h>  
  7. #include <errno.h>  
  8. #include <string.h>  
  9. #include <unistd.h>  
  10.   
  11. #define MAX_NUM_ARG 10  
  12. void exeCmd(int sockClient, char * cmd)  
  13. {  
  14.       
  15.     if(fork()==0)  
  16.     {  
  17.         //Set redirection  
  18.         dup2(sockClient, 0);  
  19.         dup2(sockClient, 1);  
  20.         close(sockClient);  
  21.   
  22.         execlp("bash""bash""-c", cmd, NULL);  
  23.         exit(0);  
  24.     }  
  25. }  
  26.   
  27. int main(int argc, char *argv[])  
  28. {  
  29.       
  30.   
  31.     int numPort, lenMsg, i;  
  32.     int sockServ, sockClient;  
  33.     struct sockaddr_in adrServ, adrClient;  
  34.     char message[256];  
  35.     char * overMsg = "over";  
  36.     int adrSize = sizeof(adrClient);  
  37.     int yes = 1;  
  38.   
  39.     if(argc < 2)  
  40.     {  
  41.         printf("syntaxe : %s numPort\n", argv[0]);  
  42.         return -1;  
  43.     }  
  44.       
  45.   
  46.     //set local addr and port  
  47.     numPort = atoi(argv[1]);  
  48.     adrServ.sin_addr.s_addr = INADDR_ANY;  
  49.     adrServ.sin_family = AF_INET;  
  50.     adrServ.sin_port = htons(numPort);  
  51.     bzero(&(adrServ.sin_zero), 8) ;  
  52.       
  53.     //Get a socket  
  54.     if((sockServ = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("sockServet"); return -1; }  
  55.       
  56.     //We can set some param here.// paramètrage de la socket pour réutiliser le N° ce port sans délai  
  57.     if(setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); return -1; }  
  58.   
  59.     // bind  
  60.     if(bind(sockServ, (struct sockaddr *)(&adrServ), sizeof(adrServ)) == -1) { perror("bind"); return -1; }  
  61.   
  62.     // place la socket en écoute  
  63.     if(listen(sockServ, 1) == -1) { perror("listen"); return -1; }  
  64.       
  65.   
  66.     // wait for a connection from a client  
  67.     if((sockClient = accept(sockServ, (struct sockaddr *)(&adrClient), &adrSize)) == -1) { perror("accept"); return -1; }  
  68.     printf("New socket client : %s\n", inet_ntoa(adrClient.sin_addr));  
  69.   
  70.       
  71.     do  
  72.     {  
  73.         if((lenMsg = recv(sockClient, message, 256, 0)) == -1)  
  74.             { perror("recv"); return -1; }  
  75.         else if (lenMsg == 0)  
  76.         {  
  77.             close(sockClient);  
  78.             return 0;  
  79.         }  
  80.         message[lenMsg] = '\0';  
  81.         printf("New cmd received : %s\n", message);  
  82.         exeCmd(sockClient, message);  
  83.           
  84.     }while(strcmp(message, overMsg) != 0);    
  85.     // fermeture de la connexion  
  86.     close(sockClient);  
  87.       
  88.     return 0;  
  89. }  

运行结果:
client:每条命令下显示的结果其实是在server端执行的。你发现什么变化没有?当我们执行find / -name "socket.h" 这条耗时的命令后,server端并不能马上返回结果,而我们仍可以马上执行下一条命令:date,并且我们先得到了date的结果,之后才是find命令的搜索结果。
[plain]  view plain copy
  1. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./4_client 127.0.0.1 9999  
  2.   
  3. ls  
  4. excute_cmd_in_distance$  
  5. 12_client.c  
  6. 12_server.c  
  7. 3_client.c  
  8. 3_server.c  
  9. 4  
  10. 4_client  
  11. 4_client.c  
  12. 4_poll_client.c  
  13. 4_poll_client.c~  
  14. 4_poll_server.c  
  15. 4_poll_server.c~  
  16. 4_server.c  
  17. 4_server.c~  
  18. client4  
  19. clientPoll  
  20. doc  
  21. server4  
  22. td6.pdf  
  23.   
  24. sudo find / -name "socket.h"  
  25. excute_cmd_in_distance$date  
  26.   
  27. mercredi 26 décembre 2012, 17:53:30 (UTC+0100)  
  28.   
  29. excute_cmd_in_distance$  
  30. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/alpha/include/asm/socket.h  
  31. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/arm/include/asm/socket.h  
  32. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/avr32/include/asm/socket.h  
  33. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/blackfin/include/asm/socket.h  
  34. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/cris/include/asm/socket.h  
  35. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/frv/include/asm/socket.h  
  36. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/h8300/include/asm/socket.h  
  37. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/ia64/include/asm/socket.h  
  38. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/m32r/include/asm/socket.h  
  39. /media/DONNEES/Polytech/Tutorial/Linux/linux-2.6.33.20/linux-2.6.33.20/arch/m68k/include/asm/socket.h  
  40. excute_cmd_in_distance$  

server:
[plain]  view plain copy
  1. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./4  
  2. syntaxe : ./4 numPort  
  3. administrateur@ordicentre:~/Bureau/POLYTECH/unix/TD UNIX2012/td6$ ./4 9999  
  4. New socket client : 127.0.0.1  
  5. New cmd received : ls  
  6. New cmd received : sudo find / -name "socket.h"  
  7. [sudo] password for administrateur:   
  8. New cmd received : date  

可以看出server先后收到的所有命令。另外,由于重定向是在子进程中做得,所以要求输入密码的那行并没有发送给客户端而是在本地显示了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值