一、实验目的
理解进程的创建和终止方法;
熟悉父进程与子进程对描述符的操作过程;
学会编写基本的多进程并发服务器程序和客户程序。
二、实验平台
ubuntu-8.04操作系统
三、实验内容
编写多进程并发服务器程序和客户程序,具体功能如下:
1、服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。
2、客户首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,继续等待用户输入字符串,直到用户输入Ctrl+D,客户关闭连接并退出。
四、实验原理
前面所实现的服务器/客户程序中,服务器每次只能处理一个客户的请求,他虽然很简单但效率低下。在实际应用中,这样的服务器不能满足实际需求,并发技术可以极大地提高服务器的处理能力和响应速度。
TCP并发服务器的工作流程见图6.1所示:
图6.1TCP并发服务器
1、创建进程
可以通过调用fork或vfork函数来创建新进程。
(1)fork函数
------------------------------------------------------------------- #include<sys/types.h> #include <unistd.h> pid_t fork(void) 返回:父进程中返回子进程的进程ID,子进程返回0;-1—出错 ------------------------------------------------------------------- |
fork后,子进程和父进程继续执行fork()函数后的指令。子进程是父进程的副本。子进程拥有父进程的数据空间、堆栈的副本。但父、子进程并不共享这些存储空间部分。如果代码段是只读的,则父子进程共享代码段。如果父子进程同时对同一文件描述字操作,而又没有任何形式的同步,则会出现混乱的状况;
父进程中调用fork之前打开的所有描述字在函数fork返回之后子进程会得到一个副本。fork后,父子进程均需要将自己不使用的描述字关闭。
(2)vfork函数
------------------------------------------------------------------- #include<sys/types.h> #include <unistd.h> pid_tvfork(void) 返回:父进程中返回子进程的进程ID,子进程返回0;-1—出错 ------------------------------------------------------------------- |
2、终止进程
进程的终止存在两个可能:
(1)父进程先于子进程终止;
(2)子进程先于主进程终止。
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,否则由参数pid和option共同决定函数行为,其中pid参数意义如下:
Option最常用的选项是WNOHANG,它通知内核在没有已终止进程时不要堵塞。
调用wait或waitpid函数时,正常情况下,可能会有以下几种情况:
五、实验步骤
1、登陆进入ubuntu操作系统,新建一个文件,命名为mproc_server.c,新建另一个文件,命名为mproc_client.c。
2、在mproc_server.c和mproc_client.c中编写相应代码并保存。
3、打开一个“终端”,执行命令进入mproc_server.c和mproc_client.c所在目录。
4、执行命令gcc–omproc_servermproc_server.c生成可执行文件mproc_server。
5、执行命令gcc–omproc_clientmproc_client.c生成可执行文件mproc_client。
6、执行命令./mproc_server,运行服务器端。
7、打开第2个“终端”,执行命令进入mproc_server.c和mproc_client.c所在目录。
8、执行命令./mproc_client127.0.0.1,模拟客户1。
9、打开第3个“终端”,执行命令进入mproc_server.c和mproc_client.c所在目录。
10、执行命令./mproc_client127.0.0.1,模拟客户2。
11、程序运行结果如下:
服务器端:
客户1:
客户2:
12、在客户端按下Ctrl+D,关闭客户连接。
13、认真分析源代码,体会多进程并发服务器程序的编写。
六、参考程序
1、mproc_server.c内容如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-
- #define PORT 1234
- #define BACKLOG 5
- #define MAXDATASIZE 1000
- void process_cli(int connfd, struct sockaddr_in client);
-
- main()
- {
- int listenfd, connfd;
- pid_t pid;
- struct sockaddr_in server;
- struct sockaddr_in client;
- int len;
-
- if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- perror("Creatingsocket failed.");
- exit(1);
- }
-
- int opt =SO_REUSEADDR;
- setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
- bzero(&server,sizeof(server));
- server.sin_family=AF_INET;
- server.sin_port=htons(PORT);
- server.sin_addr.s_addr= htonl (INADDR_ANY);
- if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {
- perror("Bind()error.");
- exit(1);
- }
-
- if(listen(listenfd,BACKLOG)== -1){
- perror("listen() error\n");
- exit(1);
- }
- len=sizeof(client);
-
- while(1)
- {
- if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {
- perror("accept() error\n");
- exit(1);
- }
- if ((pid=fork())>0){
- close(connfd);
- continue;
- }
- else if (pid==0) {
- close(listenfd);
- process_cli(connfd, client);
- exit(0);
- }
- else {
- printf("fork()error\n");
- exit(0);
- }
- }
- close(listenfd);
- }
-
- void process_cli(int connfd, struct sockaddr_in client)
- {
- int num;
- char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
- printf("Yougot a connection from %s. ",inet_ntoa(client.sin_addr) );
- num = recv(connfd,cli_name, MAXDATASIZE,0);
- if (num == 0)
- {
- close(connfd);
- printf("Client disconnected.\n");
- return;
- }
- cli_name[num - 1] ='\0';
- printf("Client'sname is %s.\n",cli_name);
-
- while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {
- recvbuf[num] ='\0';
- printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);
- int i = 0;
- for (i = 0;i < num - 1; i++) {
- if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))
- {
- recvbuf[i]=recvbuf[i]+ 3;
- if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))
- recvbuf[i]=recvbuf[i]- 26;
- }
- sendbuf[i] =recvbuf[i];
- }
- sendbuf[num - 1]= '\0';
-
- send(connfd,sendbuf,strlen(sendbuf),0);
- }
- close(connfd);
- }
2、mproc_client.c内容如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include<sys/types.h>
- #include<sys/socket.h>
- #include<netinet/in.h>
- #include <netdb.h>
-
- #define PORT 1234
- #define MAXDATASIZE100
- void process(FILE*fp, int sockfd);
- char *getMessage(char* sendline,int len, FILE* fp);
-
- int main(int argc,char *argv[])
- {
- int fd;
- struct hostent *he;
- struct sockaddr_in server;
-
- if (argc !=2) {
- printf("Usage:%s <IP Address>\n",argv[0]);
- exit(1);
- }
-
- if((he=gethostbyname(argv[1]))==NULL){
- printf("gethostbyname() error\n");
- exit(1);
- }
- if((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){
- printf("socket()error\n");
- exit(1);
- }
-
- bzero(&server,sizeof(server));
- server.sin_family =AF_INET;
- server.sin_port=htons(PORT);
- server.sin_addr= *((struct in_addr *)he->h_addr);
-
- if(connect(fd,(struct sockaddr *)&server,sizeof(server))==-1){
- printf("connect() error\n");
- exit(1);
- }
-
- process(stdin,fd);
-
- close(fd);
- }
-
- void process(FILE *fp, int sockfd)
- {
- char sendline[MAXDATASIZE],recvline[MAXDATASIZE];
- int num;
-
- printf("Connected to server. \n");
- printf("Input client's name : ");
- if (fgets(sendline, MAXDATASIZE, fp) == NULL) {
- printf("\nExit.\n");
- return;
- }
- send(sockfd,sendline, strlen(sendline),0);
- while(getMessage(sendline, MAXDATASIZE, fp) != NULL) {
- send(sockfd,sendline, strlen(sendline),0);
-
- if ((num =recv(sockfd, recvline, MAXDATASIZE,0)) == 0) {
- printf("Server terminated.\n");
- return;
- }
-
- recvline[num]='\0';
- printf("Server Message: %s\n",recvline);
-
- }
- printf("\nExit.\n");
- }
-
- char *getMessage(char* sendline,int len, FILE* fp)
- {
- printf("Inputstring to server:");
- return(fgets(sendline,MAXDATASIZE, fp));
- }