@zwh8800
2017-08-23T02:04:44.000000Z
字数 5106
阅读 191170
blog 归档 unix 网络编程 系统编程
功能:聊天室服务器,可用telnet直接连接到服务器,发送消息则在聊天室内的所有用户都能看见。使用select实现
功能:聊天室服务器
可用telnet直接连接到服务器,发送消息则在聊天室内的所有用户都能看见。
使用select实现
#include <sys/select.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <stddef.h>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <errno.h>#define max(a, b) ((a) > (b) ? (a) : (b))struct filedescriptor_node{int fd;struct filedescriptor_node* next;};struct filedescriptor_set{size_t len;struct filedescriptor_node* head;struct filedescriptor_node* iter;int max_fd;fd_set fds;};int fds_init(struct filedescriptor_set* _this){bzero(_this, sizeof(struct filedescriptor_set));FD_ZERO(&_this->fds);return 0;}struct filedescriptor_set*fds_create(){struct filedescriptor_set* _this;_this = malloc(sizeof(struct filedescriptor_set));if (_this == NULL)return NULL;if (fds_init(_this) == -1){free(_this);return NULL;}return _this;}int fds_add(struct filedescriptor_set* _this, int fd){struct filedescriptor_node* p;p = malloc(sizeof(struct filedescriptor_node));if (p == NULL)return -1;p->fd = fd;p->next = _this->head;_this->head = p; /* 在head前插入新节点 */++_this->len;if (fd > _this->max_fd)_this->max_fd = fd;FD_SET(fd, &_this->fds);return 0;}int fds_remove(struct filedescriptor_set* _this, int fd){struct filedescriptor_node* p;if (_this->head->fd == fd){p = _this->head;_this->head = p->next;free(p);}else{for (p = _this->head; p->next != NULL; p = p->next)if (p->next->fd == fd)break; /* 令p指向fd的前一个 */if (p->next == NULL)return -1;}--_this->len;if (fd == _this->max_fd){_this->max_fd = 0; /* _this->max_fd = _this->head->fd不正确,this->head可能为NULL */for (p = _this->head; p != NULL; p = p->next)if (p->fd > _this->max_fd)_this->max_fd = p->fd;}FD_CLR(fd, &_this->fds);return 0;}fd_set fds_getfdset(struct filedescriptor_set* _this){return _this->fds;}int fds_getmaxfd(struct filedescriptor_set* _this){return _this->max_fd;}void fds_iter_init(struct filedescriptor_set* _this){_this->iter = _this->head;}int fds_iter_next(struct filedescriptor_set* _this){if (_this->iter != NULL){int fd = _this->iter->fd;_this->iter = _this->iter->next;return fd;}elsereturn -1;}void fds_destroy(struct filedescriptor_set* _this){struct filedescriptor_node *p, *q;p = _this->head;q = p->next;while (p != NULL){free(p);p = q;if (q != NULL)q = q->next;}}void fds_delete(struct filedescriptor_set* _this){fds_destroy(_this);free(_this);}int writen(int fd, char* buf, size_t len){ssize_t n;size_t left = len;while (left > 0){if ((n = write(fd, buf, len)) <= 0){if (errno == EINTR)continue;elsereturn -1;}left -= n;buf += n;}return len; /* 无论如何也要写完,否则就是错误 */}int readn(int fd, char* buf, size_t len){ssize_t n;size_t left = len;while (left > 0){if ((n = read(fd, buf, len)) < 0){if (errno == EINTR)continue;elsereturn -1;}else if (n == 0) /* 到达EOF */{break;}left -= n;buf += n;}return len - left; /* 可能提前到达EOF */}#define PORT 10086#define BUF_SIZE 4096typedef struct sockaddr SA;int main(int argc, char* argv[]){struct filedescriptor_set fds;int svrsock;struct sockaddr_in svraddr;svrsock = socket(AF_INET, SOCK_STREAM, 0);if (svrsock == -1){perror("socket");exit(errno);}bzero(&svraddr, sizeof(svraddr));svraddr.sin_family = AF_INET;svraddr.sin_port = htons(PORT);svraddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(svrsock, (SA*)&svraddr, sizeof(svraddr)) == -1){perror("bind");exit(errno);}if (listen(svrsock, 64) == -1){perror("listen");exit(errno);}if (fds_init(&fds) == -1){perror("fds_init");exit(errno);}while (1){int fd;fd_set fdset;char buf[BUF_SIZE];size_t buflen;fdset = fds_getfdset(&fds);FD_SET(svrsock, &fdset);if (select(max(fds_getmaxfd(&fds), svrsock) + 1, &fdset, NULL, NULL, NULL) == -1){perror("select");exit(errno);}if (FD_ISSET(svrsock, &fdset)){int cltsock;struct sockaddr_in cltaddr;socklen_t cltaddrlen = sizeof(cltaddr);char cltaddrstr[INET_ADDRSTRLEN];if ((cltsock = accept(svrsock, (SA*)&cltaddr, &cltaddrlen)) == -1){perror("accept");exit(errno);}if (inet_ntop(AF_INET, &cltaddr.sin_addr, cltaddrstr, INET_ADDRSTRLEN) == NULL){perror("inet_ntop");exit(errno);}/* 通知新用户到达 */printf("incoming connection from %s:%d\n", cltaddrstr, ntohs(cltaddr.sin_port));snprintf(buf, BUF_SIZE, "user %s:%d joined\n", cltaddrstr, ntohs(cltaddr.sin_port));buflen = strlen(buf);for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;){if (writen(fd, buf, buflen) == -1){perror("writen");exit(errno);}}strncpy(buf, "Welcom\n", BUF_SIZE);buflen = strlen(buf);if (writen(cltsock, buf, buflen) == -1){perror("writen");exit(errno);}if (fds_add(&fds, cltsock) == -1){perror("fds_add");exit(errno);}}for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;){if (FD_ISSET(fd, &fdset)){int n;struct filedescriptor_node* old;if ((n = readn(fd, buf, BUF_SIZE)) == -1){perror("readn");exit(errno);}else if (n == 0){/*struct sockaddr_in cltaddr;socklen_t cltaddrlen = sizeof(cltaddr);char cltaddrstr[INET_ADDRSTRLEN];if (getpeername(fd, (SA*)&cltaddr, &cltaddrlen) == -1){perror("getpeername");exit(errno);}if (inet_ntop(AF_INET, &cltaddr.sin_addr, cltaddrstr, INET_ADDRSTRLEN) == NULL){perror("inet_ntop");exit(errno);}printf("%s disconnected the connection\n", cltaddrstr);snprintf(buf, BUF_SIZE, "user %s exited\n", cltaddrstr);buflen = strlen(buf);for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;){if (writen(fd, buf, buflen) == -1){perror("writen");exit(errno);}}*/fds_remove(&fds, fd);continue;}old = fds.iter;for (fds_iter_init(&fds); (fd = fds_iter_next(&fds)) >= 0;){if (writen(fd, buf, n) == -1){perror("writen");exit(errno);}}fds.iter = old;}}}fds_destroy(&fds);return 0;}
