多进程并发服务器
本实验基于Linux平台下的C语言编程实现
1.服务器端程序设计步骤:
(1)使用socket()函数创建套接字;
(2)将创建的套接字绑定到指定的地址结构;
(3)listen()函数设置套接字为侦听模式,使服务器处于打开状态;
(4)接受客户端的连接请求,建立连接;
(5)接收、应答客户端的数据请求;
(6)终止连接。
2. 客户端程序设计步骤:
(1)使用socket()函数创建套接字;
(2)调用connect()函数建立一个与TCP服务器的连接;
(3)发送数据请求,接收服务器的数据应答;
(6)终止连接。
代码:
//客户端: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define MAXDATASIZE 1000 #define PORT 1234 int main(int argc,char **argv){ int listenfd,nbytes; struct sockaddr_in srvaddr; char buf[MAXDATASIZE]; int i=1; //使用socket()函数创建套接字 listenfd=socket(AF_INET,SOCK_STREAM,0); //listenfd侦听套接字,SOCK_STREAM流式套接字 if(listenfd==-1){ printf("create socket failed !\n"); exit(1); } //指定服务器地址(本地socket地址采用默认值) bzero(&srvaddr,sizeof(srvaddr)); //初始化 srvaddr.sin_family=AF_INET; //IPv4 srvaddr.sin_port=htons(PORT); //htons:host to net long //将字符串形式的IP地址转换成32网络字节序的IP地址 if(inet_aton("127.0.0.1",srvaddr.sin_addr.s_addr)==-1){ printf("addr convert error !\n"); exit(1); } //调用connect()函数建立一个与TCP服务器的连接 if(connect(listenfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))==-1){ printf("connect error !\n"); exit(1); } //接收服务器的数据应答 //recv()函数前三个参数与read()函数一致,第四个参数为0时与read()相同 if((nbytes=recv(listenfd,buf,MAXDATASIZE,0))==-1){ printf("read error !\n"); exit(1); } buf[nbytes]='\0'; printf("Server Massage: %s\n",buf); printf("Input your name: "); //提示输入客户姓名,用来区分不同的客户 scanf("%s",buf); if((nbytes=send(listenfd,buf,strlen(buf),0))==-1){ perror("Send error !\n"); exit(1); } while(i){ printf("Input message(max char:%d):",MAXDATASIZE);//输入要发给server的消息 scanf("%s",buf); if(strlen(buf)<1) i=0; if((nbytes=send(listenfd,buf,strlen(buf),0))==-1){//发送消息 perror("Send error !\n"); exit(1); } if((nbytes=recv(listenfd,buf,MAXDATASIZE,0))==-1){//接收server的反馈消息 perror("read error !\n"); exit(1); } buf[nbytes]='\0'; printf("Server message: %s\n",buf); printf("\n"); } //关闭listenfd close(listenfd); }
//服务器端: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #define MAXDATASIZE 1000 #define PORT 1234 #define BACKLOG 5 void process_client(int connectfd,struct sockaddr_in client); int main(int argc,char **argv){ int listenfd,connectfd,nbytes,sin_size; char buf[MAXDATASIZE]; int opt=SO_REUSEADDR; pid_t pid; struct sockaddr_in srvaddr,clientaddr; //创建网络端点 listenfd=socket(AF_INET,SOCK_STREAM,0);//listenfd为侦听套接字,SOCK_STREAM流式 if(listenfd==-1){ printf("Create socket failed !\n"); exit(1); } //填充地址 bzero(&srvaddr,sizeof(srvaddr)); //初始化 srvaddr.sin_family=AF_INET; //IPv4 srvaddr.sin_port=htons(PORT); //htons:host to net short //将字符串形式的IP地址转换成32网络字节序的IP地址 if(inet_aton("127.0.0.1",srvaddr.sin_addr.s_addr)==-1){ printf("addr convert error !\n"); exit(1); } //绑定服务器地址和端口 if(bind(listenfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))==-1){ printf("Bind error !\n"); exit(1); } //监听端口 if(listen(listenfd,BACKLOG)==-1){ printf("listen error !\n"); exit(1); } while(1){ //接收客户端的连接请求,建立连接 if((connectfd=accept(listenfd,(struct sockaddr *)&clientaddr,&sin_size))==-1){ perror("Accept error !\n"); exit(-1); } //下面是多进程并发的核心所在 //用fork()函数创建进程 if((pid=fork())>0){ //父进程处理过程 close(connectfd); //父进程关闭已连接描述符 continue; //继续接受下一个客户的请求 } else if(pid==0){ //子进程处理过程 //子进程关闭监听描述符,然后调用process_client()函数处理该客户的请求 close(listenfd); process_client(connectfd,clientaddr); exit(1); //终止子进程 } else{ //调用失败处理 perror("Fork error !\n"); exit(0); //退出 } } close(listenfd); } void process_client(int connectfd,struct sockaddr_in client){ char recvbuf[MAXDATASIZE]; char client_name[MAXDATASIZE]; int recvlen,i; //显示客户的IP,用inet_ntoa将客户地址转换为可显示的 printf("You get a connetion from %s\n",inet_ntoa(client.sin_addr)); send(connectfd,"Welcome to my server.\n",22,0); //接收客户的名字并显示 recvlen=recv(connectfd,client_name,MAXDATASIZE,0); //对是否已经结束通话进行判断 if(recvlen==0){ close(connectfd); printf("Client disconnected.\n"); return; } else if(recvlen<0){ close(connectfd); printf("Connect broked.\n"); return; } client_name[recvlen]='\0'; printf("Client name is %s.\n",client_name); //不断重复接收客户的数据,接收成功则显示 while(recvlen=recv(connectfd,recvbuf,MAXDATASIZE,0)){ recvbuf[recvlen]='\0'; printf("Receive from client<%s> message: %s\n",client_name,recvbuf); recvbuf[recvlen]='\0'; send(connectfd,recvbuf,strlen(recvbuf),0); } printf("Client: %s disconnected.\n",client_name); //关闭connectfd close(connectfd); }
运行结果如下:
①开启服务器
服务器端:
②客户A连接服务器
客户端A:
服务器端:
③客户B也连接服务器
客户端B:
服务器端:
④客户B发送消息
客户端B发送hello!:
服务器端收到hello!并显示是B发来的:
⑤客户A发送消息
客户端A发送thanks.
服务器端接收到thanks.并显示是来自A的消息:
⑥客户端A断开连接
客户端退出:
服务器端显示如下:
⑦客户端B断开连接
客户端退出:
服务器端显示如下: