多线程客户端 linux,【linux下c语言服务器开发系列1】多线程处理多客户端的连接...

在上文中,提供了一个十分简单的回射服务器的demo,但是,这个demo的作用十分有限,只能接收一个客户端的连接,然后和其建立联系,进行通讯。等这个客户端断开连接后,才能处理另外的连接。功能太弱了。因为我们之前学过多进程程序设计,所以,这里提供一个回射服务器的多进程版本,使之能够同时处理多个客户端的连接。

多进程主要是靠fork函数来完成的,他是一个特殊函数,一次调用,两次返回。如果返回大于0,表示是父进程,同时返回值表示了他的子进程的pid。返回值如果等于0,表示他是一个子进程。子进程是父进程的一个拷贝,保留了父进程的所有东西。但是,我们往往需要让子进程执行别的函数。这个函数,你可以在程序里写,然后在子进程调用;也可以是已经存在的文件,你可以用exec族的函数,对子进程的内容进行复写。这部分具体怎么处理,完全看你的业务需求。在本例中,就是对已经接受的连接,进行读写操作。

在返回的父进程中,往往需要继续处理程序的主框架的内容。而且,需要对子进程的资源进行回收,即等待子进程执行完成,用wait和waitpid函数回收子进程,避免其成为僵尸进程而占用资源。wait可以返回子进程的退出状态,waipid可以非阻塞的处理。是wait的加强版。

下面是这个版本的回射服务器的demo代码,贴一下。注意,因为代码没有重构,看起来比较乱。尤其是读写函数,目前还处于一个零散的状态,没有抽出来。此外,没有进行wait,否则容易卡住,这个接下来会提供解决的方法。

#include"head.h"

#define MAX_USER 1024

#define BUF_SIZE 1024

typedef struct user //yon {

int conn; //表示客户与服务器的连接

sockaddr_in client_addr; //用户的地址

char bufread[BUF_SIZE]; // 用户的读缓存

char bufwrite[BUF_SIZE]; //用户的写缓存

} user;

int main(int argc, char **argv)

{

if(argc<3)

{

printf("usage: %s ip port\n",basename(argv[0]));

exit(0);

}

printf("start echoback server...\n");

char * ip=(char * ) argv[1];

int port =atoi(argv[2]);

/*构造服务器端的地址,主要是填充sockaddr_in 结构体*/

struct sockaddr_in server_address;

bzero(&server_address,sizeof(server_address));

server_address.sin_family=AF_INET;

inet_pton(AF_INET,ip,&server_address.sin_addr);

server_address.sin_port=htons(port);

int listenfd=socket(AF_INET,SOCK_STREAM,0);

assert(listenfd>=0);

int reuse=1;

setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));

setnonblock(listenfd);

int ret=bind(listenfd,(struct sockaddr *) &server_address, sizeof(server_address));

assert(ret==0);

ret=listen(listenfd,5);

assert(ret==0);

/*服务器服务启动,等待客户端的链接的到来*/

int run_flag=1;

char buf[1024];

int conn=-1;

user users[MAX_USER];

int stop[MAX_USER];

for(int i=0;i

stop[i]=0;

int user_number=0;

while(run_flag)

{

int acfd=accept(listenfd,NULL,NULL);

if(acfd>=0)

{

printf("acfd>0,hhahahaha");

user_number++;

/*users[user_number] 表示一个conn,唯一标识这个用户链接*/

users[user_number].conn=acfd;

pid_t pid=fork();

if(pid<0)

{

printf("fork error\n");

exit(0);

}

else if (pid==0) /*这里进入子进程*/

{

close(listenfd); //关闭无用的监听套接字,由父进程继续监听

setnonblock(users[user_number].conn);

//while(!stop[ users[user_number].conn] )

while(1)

{

int readn=read(users[user_number].conn,users[user_number].bufread,BUF_SIZE);

// printf("readn=%d\n",readn);

//printf("errno=%d\n",errno);

if( (readn<0) && (errno!=EAGAIN))

{

printf("a\n");

printf("read fail, client %d\n",users[user_number].conn);

close(users[user_number].conn);

//stop[ users[user_number].conn ]=1;

//break;

exit(0);

}

else if (readn==0)

{

printf("b\n");

close(users[user_number].conn);

//stop[ users[user_number].conn ]=1;

//break;

exit(0);

} else if (readn>0)

{

printf("pid= %d\n",getpid());

users[user_number].bufread[readn]='\0';

memcpy(users[user_number].bufwrite,users[user_number].bufread,sizeof(users[user_number].bufread));

int writen=write(users[user_number].conn,users[user_number].bufwrite,sizeof(users[user_number].bufwrite));

memset(users[user_number].bufread,0,sizeof(users[user_number].bufread));

printf("write %d bytes to %d\n",writen,users[user_number].conn);

}

}

}

/*parent process*/

else

{

close(users[user_number].conn);

int stat_loc;

/*如果想要多个进程共同服务的话,就不能在这里阻塞的等待,可以通过sigchld信号进行捕获*/

// wait((int *)&stat_loc);

printf("parent, finished wait\n");

}

}

}

}

这段代码,运行起来后,可以用多个telnet连接服务器,发现服务器能为多个客户端服务啦。代码中存在的缺点如读写逻辑差、未进行子进程回收等内容,将在下一个版本中改掉。而且,多进程一般是用进程池,后面的版本也会实现。敬请期待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值