多进程并发服务器编程

多进程并发服务器编程


一、实验目的

理解进程的创建和终止方法;

熟悉父进程与子进程对描述符的操作过程;

学会编写基本的多进程并发服务器程序和客户程序。

二、实验平台

ubuntu-8.04操作系统

三、实验内容

编写多进程并发服务器程序和客户程序,具体功能如下:

1、服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。

2、客户首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,继续等待用户输入字符串,直到用户输入Ctrl+D,客户关闭连接并退出。

四、实验原理

前面所实现的服务器/客户程序中,服务器每次只能处理一个客户的请求,他虽然很简单但效率低下。在实际应用中,这样的服务器不能满足实际需求,并发技术可以极大地提高服务器的处理能力和响应速度。

TCP并发服务器的工作流程见图6.1所示:

6.1TCP并发服务器

1、创建进程

可以通过调用forkvfork函数来创建新进程。

1fork函数

-------------------------------------------------------------------
#include<sys/types.h>

#include <unistd.h>

pid_t fork(void)

返回:父进程中返回子进程的进程ID,子进程返回0-1出错

-------------------------------------------------------------------


  • fork后,子进程和父进程继续执行fork()函数后的指令。子进程是父进程的副本。子进程拥有父进程的数据空间、堆栈的副本。但父、子进程并不共享这些存储空间部分。如果代码段是只读的,则父子进程共享代码段。如果父子进程同时对同一文件描述字操作,而又没有任何形式的同步,则会出现混乱的状况;

  • 父进程中调用fork之前打开的所有描述字在函数fork返回之后子进程会得到一个副本。fork后,父子进程均需要将自己不使用的描述字关闭。

2vfork函数

-------------------------------------------------------------------
#include<sys/types.h>

#include <unistd.h>

pid_tvfork(void)

返回:父进程中返回子进程的进程ID,子进程返回0-1出错

-------------------------------------------------------------------


  • forkvfork函数的基本区别在于当使用vfork()创建新进程时,父进程将被暂时阻塞,而子进程则可以借用父进程的地址空间,直到子进程退出,至此父进程才继续执行。

2、终止进程

进程的终止存在两个可能:

1)父进程先于子进程终止;

2)子进程先于主进程终止。

  • 对于后者,系统内核为子进程保留一定的状态信息:进程ID、终止状态、CPU时间等;当父进程调用waitwaitpid函数时,获取这些信息。

  • 当子进程正常或异常终止时,系统内核向其父进程发送SIGCHLD信号;缺省情况下,父进程忽略该信号,或者提供一个该信号发生时即被调用的函数。

exit()函数:

-------------------------------------------------------------------
#include<stdlib.h>

void exit(int status);

-------------------------------------------------------------------

exit()函数用于终止调用进程。关闭所有子进程打开的描述符,向父进程发送SIGCHLD信号,并返回状态。

父进程可通过调用wait()waitpid()函数获得子进程的终止信息。

wait()函数:

-------------------------------------------------------------------
#include<sys/types.h>

#include <sys/wait.h>

pid_t wait(int*stat_loc);

返回:终止子进程的ID-成功;-1-出错;stat_loc存储子进程的终止状态(一个整数);

-------------------------------------------------------------------

如果没有终止的子进程,但是有一个或多个正在执行的子进程,则该函数将堵塞,直到有一个子进程终止或者wait被信号中断时,wait返回。

当调用该系统调用时,如果有一个子进程已经终止,则该系统调用立即返回,并释放子进程所有资源。


waitpid()函数:

-------------------------------------------------------------------
#include<sys/types.h>

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int*stat_loc, int option);

返回:终止子进程的ID-成功;-1-出错;stat_loc存储子进程的终止状态;-------------------------------------------------------------------


pid=-1,option=0时,该函数等同于wait,否则由参数pidoption共同决定函数行为,其中pid参数意义如下:

    • -1:要求知道任何一个子进程的返回状态(等待第一个终止的子进程);

    • >0:要求知道进程号为pid的子进程的状态;

    • <-1:要求知道进程号为pid的绝对值的子进程的终止状态

Option最常用的选项是WNOHANG,它通知内核在没有已终止进程时不要堵塞。

调用waitwaitpid函数时,正常情况下,可能会有以下几种情况:

    • 阻塞(如果其所有子进程都还在运行);

    • 获得子进程的终止状态并立即返回(如果一个子进程已终止,正等待父进程存取其终止状态);

    • 出错立即返回(如果它没有任何子进程)


五、实验步骤

1、登陆进入ubuntu操作系统,新建一个文件,命名为mproc_server.c,新建另一个文件,命名为mproc_client.c

2、在mproc_server.cmproc_client.c中编写相应代码并保存。

3、打开一个终端,执行命令进入mproc_server.cmproc_client.c所在目录。

4、执行命令gccomproc_servermproc_server.c生成可执行文件mproc_server

5、执行命令gccomproc_clientmproc_client.c生成可执行文件mproc_client

6、执行命令./mproc_server,运行服务器端。

7、打开第2终端,执行命令进入mproc_server.cmproc_client.c所在目录。

8、执行命令./mproc_client127.0.0.1,模拟客户1

9、打开第3终端,执行命令进入mproc_server.cmproc_client.c所在目录。

10、执行命令./mproc_client127.0.0.1,模拟客户2

11、程序运行结果如下:

服务器端:

多进程并发服务器编程_第1张图片

客户1



客户2


12、在客户端按下Ctrl+D,关闭客户连接。

13、认真分析源代码,体会多进程并发服务器程序的编写。

六、参考程序

1mproc_server.c内容如下:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7. #include <netinet/in.h>  
  8. #include <arpa/inet.h>  
  9.   
  10. #define PORT 1234  
  11. #define BACKLOG 5  
  12. #define MAXDATASIZE 1000  
  13. void process_cli(int  connfd, struct sockaddr_in client);  
  14.   
  15. main()  
  16. {  
  17. int  listenfd, connfd;  
  18. pid_t  pid;  
  19. struct  sockaddr_in  server;  
  20. struct sockaddr_in  client;  
  21. int  len;  
  22.   
  23. if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
  24. perror("Creatingsocket failed.");  
  25. exit(1);  
  26. }  
  27.   
  28. int opt =SO_REUSEADDR;  
  29. setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
  30. bzero(&server,sizeof(server));  
  31. server.sin_family=AF_INET;  
  32. server.sin_port=htons(PORT);  
  33. server.sin_addr.s_addr= htonl (INADDR_ANY);  
  34. if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {  
  35. perror("Bind()error.");  
  36. exit(1);  
  37. }  
  38.   
  39. if(listen(listenfd,BACKLOG)== -1){  
  40. perror("listen() error\n");  
  41. exit(1);  
  42. }  
  43. len=sizeof(client);  
  44.   
  45. while(1)  
  46. {  
  47. if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {  
  48. perror("accept() error\n");  
  49. exit(1);  
  50. }  
  51. if ((pid=fork())>0){  
  52. close(connfd);  
  53. continue;  
  54. }  
  55. else if (pid==0) {  
  56. close(listenfd);  
  57. process_cli(connfd, client);  
  58. exit(0);  
  59. }  
  60. else {  
  61. printf("fork()error\n");  
  62. exit(0);  
  63. }  
  64. }  
  65. close(listenfd);  
  66. }  
  67.   
  68. void process_cli(int connfd, struct sockaddr_in client)  
  69. {  
  70. int num;  
  71. char  recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];  
  72. printf("Yougot a connection from %s. ",inet_ntoa(client.sin_addr) );  
  73. num = recv(connfd,cli_name, MAXDATASIZE,0);  
  74. if (num == 0)  
  75. {  
  76. close(connfd);  
  77. printf("Client disconnected.\n");  
  78. return;  
  79. }  
  80. cli_name[num - 1] ='\0';  
  81. printf("Client'sname is %s.\n",cli_name);  
  82.   
  83. while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {  
  84. recvbuf[num] ='\0';  
  85. printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);  
  86. int i = 0;  
  87. for (i = 0;i < num - 1; i++) {  
  88. if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))  
  89. {  
  90. recvbuf[i]=recvbuf[i]+ 3;  
  91. if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))  
  92. recvbuf[i]=recvbuf[i]- 26;  
  93. }  
  94. sendbuf[i] =recvbuf[i];  
  95. }  
  96. sendbuf[num - 1]= '\0';  
  97.   
  98. send(connfd,sendbuf,strlen(sendbuf),0);  
  99. }  
  100. close(connfd);  
  101. }  


 

2mproc_client.c内容如下:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <string.h>  
  5. #include<sys/types.h>  
  6. #include<sys/socket.h>  
  7. #include<netinet/in.h>  
  8. #include <netdb.h>  
  9.   
  10. #define PORT 1234  
  11. #define MAXDATASIZE100  
  12. void process(FILE*fp, int sockfd);  
  13. char *getMessage(char* sendline,int len, FILE* fp);  
  14.   
  15. int main(int argc,char *argv[])  
  16. {  
  17. int fd;  
  18. struct hostent  *he;  
  19. struct sockaddr_in  server;  
  20.   
  21. if (argc !=2) {  
  22. printf("Usage:%s <IP Address>\n",argv[0]);  
  23. exit(1);  
  24. }  
  25.   
  26. if((he=gethostbyname(argv[1]))==NULL){  
  27. printf("gethostbyname() error\n");  
  28. exit(1);  
  29. }  
  30. if((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){  
  31. printf("socket()error\n");  
  32. exit(1);  
  33. }  
  34.   
  35. bzero(&server,sizeof(server));  
  36. server.sin_family =AF_INET;  
  37. server.sin_port=htons(PORT);  
  38. server.sin_addr= *((struct in_addr *)he->h_addr);  
  39.   
  40. if(connect(fd,(struct sockaddr *)&server,sizeof(server))==-1){  
  41. printf("connect() error\n");  
  42. exit(1);  
  43. }  
  44.   
  45. process(stdin,fd);  
  46.   
  47. close(fd);  
  48. }  
  49.   
  50. void process(FILE *fp, int  sockfd)  
  51. {  
  52. char sendline[MAXDATASIZE],recvline[MAXDATASIZE];  
  53. int num;  
  54.   
  55. printf("Connected to server. \n");  
  56. printf("Input client's name : ");  
  57. if (fgets(sendline, MAXDATASIZE, fp) == NULL) {  
  58. printf("\nExit.\n");  
  59. return;  
  60. }  
  61. send(sockfd,sendline, strlen(sendline),0);  
  62. while(getMessage(sendline, MAXDATASIZE, fp) != NULL) {  
  63. send(sockfd,sendline, strlen(sendline),0);  
  64.   
  65. if ((num =recv(sockfd, recvline, MAXDATASIZE,0)) == 0) {  
  66. printf("Server terminated.\n");  
  67. return;  
  68. }  
  69.   
  70. recvline[num]='\0';  
  71. printf("Server Message: %s\n",recvline);  
  72.   
  73. }  
  74. printf("\nExit.\n");  
  75. }  
  76.   
  77. char  *getMessage(char*  sendline,int len, FILE*  fp)  
  78. {  
  79. printf("Inputstring to server:");  
  80. return(fgets(sendline,MAXDATASIZE, fp));  
  81. }  

 

你可能感兴趣的:(多进程并发服务器编程)