導讀
本篇文章對http://www.daoluan.net/blog/?p=774中的“計算器”進行改進,與大家分享。
上面那篇中的服務端屬重復型,即一個時刻只處理一客戶的請求,處理期間不搭理其他客戶。此篇對上篇的“計算器”進行小小的改進——能夠接受多個客戶的請求。

改進細則:
- 獨立bind,listen,accept,serve(即calc過程)功能模塊;
- 所有錯誤成功提示提取至各功能模塊(函數)之外,錯誤/成功根據各函數的返回值判斷(這更符合UNIX編程風范);
- 客戶的服務過程由產生的子進程負責。
缺陷:由子進程來負責serve的部分。服務器主進程(父進程)不負責等待子進程結束,資源由內核回收(這一要非必要)。
上實驗結果圖片解解饞
服務器啟動
客戶端方面,將client1、client2和client3(編譯鏈接自同一個代碼)寫進腳本。運行
服務端服務多個客戶請求
client
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <string.h>
#define MAXSLEEP 1024
int connect_retry(int sockfd,const struct sockaddr * addr,socklen_t alen)
{
int nsec;
printf("connecting\n");
for(nsec = 1; nsec <= MAXSLEEP; nsec<<=1)
{
if(connect(sockfd,addr,alen) == 0)
{
printf("connected\n");
return 0;
}// if
if(nsec <= MAXSLEEP/2)// delay
sleep(nsec);
}// for:
return 0;
}
int main(int argc,char * argv[])
{
if(argc != 4)
{
printf("you must input 4 arg\n");
return 1;
}// if
int fd;
struct sockaddr_in si,server;
char addr[20],buf[20],bufrecv[20];
bzero(bufrecv,sizeof(bufrecv));
sprintf(addr,"127.0.0.1");
fd = socket(AF_INET,SOCK_STREAM,0);// create socker fd;
printf("socket ok\n");
//prepare server addr
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(6000);
inet_pton(AF_INET,addr,(void *)&server.sin_addr);
printf("server ok\n");
//prepare request data
bzero(buf,sizeof(buf));
sprintf(buf,"%c%c%c",argv[1][0],argv[2][0],argv[3][0]);
//connect
if(connect_retry(fd,(struct sockaddr *)&server,sizeof(server)) < 0)
{
printf("connect error\n");
return 1;
}// if
//send
if(send(fd,buf,20,0) < 0)
{
printf("client send error\n");
return 1;
}// if
//select
fd_set readfd;
FD_ZERO(&readfd);
FD_SET(fd,&readfd);
int t;
if((t = select(FD_SETSIZE,&readfd,NULL,NULL,NULL)) < 0)
{
printf("select error\n");
return 1;
}// if
//recv
bzero(bufrecv,sizeof(bufrecv));
recv(fd,bufrecv,20,0);
printf("%s\n",bufrecv);
close(fd);
return 0;
}
server
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
char bufret[20];
int initserver(int type,const struct sockaddr * addr,socklen_t alen,int qlen)
{
int fd;
int err = 0;
if((fd = socket(addr->sa_family,type,0)) < 0)
return -1;
printf("binding\n");
if(bind(fd,addr,alen) < 0)
{
err = errno;
goto errout;
}// if
printf("bind succeed \n");
if(type == SOCK_STREAM || type == SOCK_SEQPACKET)
{
printf("listening\n");
if(listen(fd,1) < 0)
{
err = errno;
printf("listen error\n");
goto errout;
}// if
}// if
printf("listened \n");
return (fd);
errout:
close(fd);
errno = err;
return -1;
}
int serve(int sockfd)
{
int a,b;
char op,buf[25];
int ret,addrlen = sizeof(struct sockaddr_in),clfd;
struct sockaddr_in client;
bzero(&client,sizeof(client));
//accept
printf("accepting\n");
clfd = accept(sockfd,(struct sockaddr *)&client,&addrlen);
//recv
printf("accepted\n");
bzero(buf,sizeof(buf));
recv(clfd,buf,20,0);
printf("recived\n");
//calculate
a = buf[0] - '0';
b = buf[1] - '0';
op = buf[2];
switch(op)
{
case '+':ret = a + b;break;
case '-':ret = a - b;break;
case '*':ret = a * b;break;
case '/':ret = a / b;break;
}// switch
sprintf(bufret,"the result:%d",ret);
//send
printf("sending\n");
if(send(clfd,bufret,20,0) < 0)
{
printf("server send error\n");
return -1;
}// if
printf("sended,server end\n");
return 0;
}
int main(int argc,char * argv[])
{
int sockfd;
char addr[20];
bzero(addr,sizeof(addr));
sprintf(addr,"127.0.0.1");
struct sockaddr_in server;
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(6000);
//server.sin_addr.s_addr = htonl(INADDR_ANY);
inet_pton(AF_INET,addr,(void *)&server.sin_addr);
//prepare server
while(1)
{
if((sockfd = initserver(SOCK_STREAM,(struct sockaddr *)&server,sizeof(server),1)) < 0)
{
printf("initserver error\n");
return 0;
}// if
pid_t pid;
if(pid = fork() < 0) // fork error
{
close(sockfd);
return -1;
}
else if(pid == 0)
{
printf("serving\n");
serve(sockfd);
break;
}// if
}// while
//serve
close(sockfd);
return 0;
}
test.sh
#!/bin/bash ./client1 1 2 + ./client2 2 3 + ./client3 3 4 +
本文完 2012-08-06




