通过Select函数实现并发
select函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段时间后唤醒它。这样我们就可以利用select函数同时监控多个连接,并且维护一个客户端连接数组用来存储连接描述符。当有客户端连接进来后,把连接符加入到此数组中,同时让select函数监控此连接述符,当有文件描述符准备好后select返回,然后轮循连接数组,判断是那个连接符已经准备好,调用客户请求处理函数,执行完成后将连接数据对应的值清空,并且将连接符从select中清除,将连接关闭。这种方法比较适合小量的并发连接。
例子:回射服务器,即服务器端接收客户端来的数据,并将数据原样返回给客户端,代码在CentOS5.0测试通过
服务器端代码:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/select.h>
#include <strings.h>
#define SERV_PORT 2003
#define MAXLINE 1024
#define LISTENQ 4
int makeListenByPort(short port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
return listenfd;
}
int main(int argc, char **argv)
{
int listenfd, connfd, maxfd, sockfd;
int client[FD_SETSIZE], nready;
int maxi;
unsigned int clilen;
ssize_t n;
char buf[MAXLINE];
struct sockaddr_in cliaddr;
inti;
fd_set rset, allset;
listenfd = makeListenByPort(SERV_PORT);
maxfd = listenfd;
maxi = -1;
for(i = 0; i < FD_SETSIZE; i++) {
client[i] = -1;
}
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for(;;) {
rset = allset;
/* select系统调用 */
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
/* 监控是否有新的连接 */
if(FD_ISSET(listenfd, &rset)) {
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
for(i = 0; i < FD_SETSIZE; i++) {
if(client[i] < 0) {
client[i] = connfd;
break;
}
}
if(i == FD_SETSIZE) {
printf("too manyclients\n");
continue;
}
FD_SET(connfd, &allset);
if(connfd > maxfd) maxfd = connfd;
/* 维护客户端的个数 */
if(i > maxi) maxi = i;
if(--nready <= 0) continue;
}
/* 轮循所有的客户端连接符 */
for(i = 0; i <= maxi; i++) {
if((sockfd = client[i]) < 0)
continue;
if(FD_ISSET(sockfd, &rset)) {
/* 如果有数据可读,则将读到的数据回写,否则清理连接 */
if((n = read(sockfd, buf,MAXLINE)) == 0) {
close(sockfd);
FD_CLR(sockfd,&allset);
client[i] = -1;
} else {
write(sockfd, buf, n);
}
if(--nready <= 0)break;
}
}
}
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#define MAXLINE 1024
#define SERV_PORT 2003
void cli_str(FILE *fp, int sockfd)
{
char sendline[MAXLINE];
char recvline[MAXLINE];
memset(recvline, 0x00, sizeof(recvline));
memset(sendline, 0x00, sizeof(sendline));
printf("cli#");
while((fgets(sendline, MAXLINE, fp)) != NULL) {
write(sockfd, sendline, strlen(sendline));
if(read(sockfd, recvline, MAXLINE) == 0) {
printf("str_cli:server terminated prematurely\n");
exit(0);
}
fputs(recvline, stdout);
fflush(stdout);
memset(recvline, 0x00, sizeof(recvline));
printf("cli#");
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("socket error\n");
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
if((connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))== -1) {
printf("connect error\n");
exit(0);
}
cli_str(stdin, sockfd);
exit(0);
}