@zwh8800
2017-08-23T10:04:44.000000Z
字数 5106
阅读 190871
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;
}
else
return -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;
else
return -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;
else
return -1;
}
else if (n == 0) /* 到达EOF */
{
break;
}
left -= n;
buf += n;
}
return len - left; /* 可能提前到达EOF */
}
#define PORT 10086
#define BUF_SIZE 4096
typedef 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;
}