本例子使用异步socket(select方法)实现了ECHO服务器程序。
搞了一个晚上,终于弄好了,出现的问题主要如下:
- 这是最重要的问题!当读取完数据后,需要将数据重新FD_SET进去,特别是serverFd,注意这个testFd意义非常重大,相当于参数传递中的复制行参,需要好好体会。
- 当read(rd)后,返回为0表示客户端的socket已经关闭,此时除了要FD_CLR,还要关闭fd!!否则fd资源没有被释放,很快就会达到select监听的上限!
[cpp]
/*
* main.cc
*
* Created on: 2009-11-30
* Author: liheyuan
* Describe: 非阻塞模式服务器(Echo服务器)
*
* Last Date: 2009-11-30
* CopyRight: 2009 @ ICT LiHeyuan
*/
#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
#define SERVER_PORT 18000
#define SERVER_QUEUE 10
#define FD_SET_SIZE 10
#define MAX_BUF 16
int main() {
//设置服务器Addr,在18000,任意IP监听
int serverFd;
serverFd = socket(AF_INET, SOCK_STREAM, 0);
/* 设置 serverFd 为非阻塞方式 */
int opt = SO_REUSEADDR;
setsockopt(serverFd, SOL_SOCKET, opt, &opt, sizeof(opt));
struct sockaddr_in serverAddr;
socklen_t serverAddrLen = sizeof(sockaddr_in);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(SERVER_PORT);
//绑定
if (bind(serverFd, (sockaddr*) &serverAddr, serverAddrLen)) {
cout << "Binding on " << SERVER_PORT << " fail." << endl;
return -1;
}
//创建等待队列
listen(serverFd, SERVER_QUEUE);
//设定fd_set
fd_set readfds, testfds;
FD_ZERO(&readfds);
FD_SET(serverFd,&readfds);
//非阻塞模式等待客户连接
struct sockaddr_in clientAddr;
int clientFd;
socklen_t clientAddrLen;
int len;
char buf[MAX_BUF];
while (1) {
FD_SET(serverFd,&readfds);
testfds = readfds;
//选择readfds中可用的fd
if (select(FD_SET_SIZE, &testfds, (fd_set *) NULL, (fd_set *) NULL,
(struct timeval *) NULL) > 0) {
if (FD_ISSET(serverFd,&testfds)) {
//如果服务器fd可用,则为accept
clientAddrLen = sizeof(sockaddr_in);
clientFd = accept(serverFd, (struct sockaddr*) &clientAddr,
&clientAddrLen);
if (clientFd == -1) {
cout << "accept() error" << endl;
return -1;
} else {
FD_SET(clientFd,&readfds);
cout << inet_ntoa(clientAddr.sin_addr) << " connect"
<< " at fd" << clientFd << endl;
}
}
//依次查询
for (int fd = 0; fd < FD_SET_SIZE; fd++) {
if (FD_ISSET(fd,&testfds)) {
//如果是Client活动,进行Echo
len = read(fd, buf, MAX_BUF);
if (len > 0) {
write(fd, buf, len);
} else {
//结束
cout << "Client end at" << clientFd << endl;
FD_CLR(fd,&readfds);
}
}
}
}
}
close(serverFd);
}
[/cpp]
你没有close客户端的fd,如果你把这句代码直接加上去的话,还有误把服务器监听accept的那个fd关了
select是同步非阻塞,不是异步