您的位置:首页 > 运维架构 > Linux

Linux中select函数的使用举例

2016-07-23 20:53 316 查看
linux中select函数的使用例子

/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
/* for select */
#include <sys/select.h>

#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#define MAX_LINE 16384

char rot13_char(char c) {
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}

struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;

int writing;
size_t n_written;
size_t write_upto;
};

struct fd_state * alloc_fd_state(void)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->buffer_used = state->n_written = state->writing =
state->write_upto = 0;
return state;
}

void free_fd_state(struct fd_state *state) {
free(state);
}

void make_nonblocking(int fd) {
fcntl(fd, F_SETFL, O_NONBLOCK);
}

/*
* result = 0时,表示连接断开,return 0;
* resut = -1时,如果errno = EAGAIN,表示阻塞式socket无数据可读,return 0;
* result = -1时,如果errno != EAGAIN, 表示读取失败,return -1.
*/
int do_read(int fd, struct fd_state *state) {
char buf[1024];
int i;
ssize_t result;
while (1) {
/* 从终端设备读,通常以行为单位,读到换行符就返回了.
* recv函数返回时,不会添加'\0'结束符,因此buffer_used用来表示实际读取到的字节数。
*/
result = recv(fd, buf, sizeof(buf), 0);
printf("do_read, fd = %d, buf = %s, strlen(buf) = %d, recv return %d, errno = %d\n", fd, buf, strlen(buf), result, errno);
if (result <= 0)
break;

for (i = 0; i < result; ++i) {
if (state->buffer_used < sizeof(state->buffer))
state->buffer[state->buffer_used++] = buf[i];
/* 如果recv中碰到‘\n’,置该socket为可写状态, 一般从终端读取时最后一个字符为‘\n’.*/
if (buf[i] == '\n') {
state->writing = 1;
state->write_upto = state->buffer_used;
}
}
}

if (result == 0) {
return 1;
} else if (result < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}

return 0;
}

/*
* 将state->write_upto个数据写入客户端,如果写入完毕,置为不可写状态.
* 写入失败则return -1;成功或发送缓冲区满则return 0.
* */
int do_write(int fd, struct fd_state *state) {
printf("do_write, fd = %d, state->written = %d, upto = %d\n", fd, state->n_written, state->write_upto);
while (state->n_written < state->write_upto) {
ssize_t result = send(fd, state->buffer + state->n_written,
state->write_upto - state->n_written, 0);
printf("do_write(), result = %d, fd = %d\n", result, fd);
if (result < 0) {
if (errno == EAGAIN) {
return 0;
}
return -1;
}
assert(result != 0);

state->n_written += result;
}

/*读到的数据已经全部写入客户端,则重置标志位*/
if (state->n_written == state->buffer_used) {
printf("Write completed\n");
state->n_written = state->write_upto = state->buffer_used = 0;
}

/*置为不可写状态*/
state->writing = 0;

return 0;
}

void run(int port)
{
struct fd_state *state[FD_SETSIZE];
struct sockaddr_in sin;
int i, maxfd;
fd_set readset, writeset, exset;

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(port);

printf("FD_SETSIZE = %d\n", FD_SETSIZE);
for (i = 0; i < FD_SETSIZE; ++i)
state[i] = NULL;

int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
printf("listen_fd = %d\n", listen_fd);
make_nonblocking(listen_fd);

#ifndef WIN32
{
int one = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

if (bind(listen_fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}

if (listen(listen_fd, 16)<0) {
perror("listen");
return;
}

FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&exset);

while (1) {
maxfd = listen_fd;

FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&exset);

FD_SET(listen_fd, &readset);

/* maxfd表示描述符集中最大的一个 */
for (i = 0; i < FD_SETSIZE; ++i) {
if (state[i]) {
if (i > maxfd) {
maxfd = i;
}

FD_SET(i, &readset);
/*writing = 1表示该socket还有未发送的数据,添加对可写状态的监听.*/
if (state[i]->writing) {
FD_SET(i, &writeset);
}
}
}

if (select(maxfd + 1, &readset, &writeset, &exset, NULL) < 0) {
perror("select");
return;
}

/*判断listen_fd是否有读事件发生,即是否有新的连接过来.*/
if (FD_ISSET(listen_fd, &readset)) {
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listen_fd, (struct sockaddr*) &ss, &slen);
if (fd < 0) {
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd);
} else {
make_nonblocking(fd);
state[fd] = alloc_fd_state();
assert(state[fd]);/*XXX*/
}
}

/*遍历0~maxfd描述符*/
for (i = 0; i < maxfd + 1; ++i) {
int r = 0;
if (i == listen_fd) {
continue;
}

if (FD_ISSET(i, &readset)) {
/*这里如果输入的数据很大, 是有可能比较耗时间的*/
r = do_read(i, state[i]);
}

if (r == 0 && FD_ISSET(i, &writeset)) {
r = do_write(i, state[i]);
}

/*读取或写入失败,则关闭该socket.*/
if (r) {
free_fd_state(state[i]);
state[i] = NULL;
close(i);
}
}
}
}

int main(int argc, char **argv) {
if(argc < 2) {
fprintf(stderr, "Usage:\n./server <port>\n");
exit(1);
}

setvbuf(stdout, NULL, _IONBF, 0);

run(atoi(argv[1]));
return 0;
}


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: