#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#define DEF_PORT 52122 //服务器默认监听端口
#define DEF_LISTEN 128
#define MAXSIZE 1024 //缓冲区的最大值
#define MAX_COUNT 256 //支持最大连接数量,不可过大,该值受多方限制,例如内核,描述符和select函数等
#define MAXLINE 1000 //can send a message maximum length
#define TYPEBUF 1 //message types buf length
/* for message type */
#define LOGIN 'L'
#define SEND 'S'
#define ERROR 'E'
#define EVFS ":;,/\\'\"[]{}~!@#$%^&*()" //不允许在名字中出现的字符,将视为违法
#define MINNAME 3
#define MAXNAME (MAXSIZE - MAXLINE - TYPEBUF -1 -2) //one '\n' char, two ':' chars
const char ** pargv;
//连接的用户的信息
struct user_info {
struct user_info *u_next;
struct user_info *u_prev;
struct sockaddr_in u_addr;
char *u_logname;
int u_fd;
int u_crflag; //断开连接标志
};
//用户信息表
struct user_list {
struct user_info *l_head;
struct user_info *l_tail;
int l_count;
};
//初始化用户信息表
void
ul_init(struct user_list *lp)
{
lp->l_head = 0;
lp->l_tail = 0;
lp->l_count = 0;
}
//分配一个用户信息对象
struct user_info *
ui_alloc(void)
{
struct user_info *pi;
pi = (struct user_info *)malloc(sizeof(struct user_info));
if (pi != 0) {
pi->u_logname = 0;
pi->u_prev = 0;
pi->u_next = 0;
pi->u_fd = -1;
pi->u_crflag = 0;
}
return(pi);
}
//添加一个用户到当前表中
struct user_info *
ui_append(struct user_list* pl, struct user_info* pu)
{
pu->u_next = 0;
pu->u_prev = pl->l_tail;
if (pl->l_tail != 0)
pl->l_tail->u_next = pu;
else
pl->l_head = pu;
pl->l_tail = pu;
pl->l_count++;
return(pu);
}
void
ui_remove(struct user_list* pl, struct user_info* pu)
{
if (pu == pl->l_head) {
pl->l_head = pu->u_next;
if (pl->l_head != 0)
pl->l_head->u_prev = 0;
if (pl->l_tail == pu)
pl->l_tail = 0;
} else if (pu == pl->l_tail) {
pl->l_tail = pu->u_prev;
if (pl->l_tail != 0)
pl->l_tail->u_next = 0;
if (pl->l_head == pu)
pl->l_head = 0;
} else {
pu->u_next->u_prev = pu->u_prev;
pu->u_prev->u_next = pu->u_next;
}
pl->l_count--;
pu->u_next = 0;
pu->u_prev = 0;
close(pu->u_fd);
pu->u_fd = -1;
}
/*
* 按用户名在列表中查找一个对象
*/
struct user_info *
ui_find(struct user_list *pl, const char* name)
{
struct user_info *pu;
for (pu = pl->l_head; pu != 0; pu = pu->u_next) {
if (pu->u_logname == 0) {
continue;
}
if (strcmp(name, pu->u_logname) == 0) {
return(pu);
}
}
return(0); //not found
}
/*
* 这个函数测试用户是否在在线列表中
*/
int
ui_isin(struct user_info* pu)
{
if (pu->u_next || pu->u_prev)
return(1);
else
return(0);
}
/*
* 释放一个对象所占用的内存,如果这个对象在列表中绝不应该被释放
* 假如不能确定是否在当前列表中,应该用上一个函数进行测试
*/
void
ui_rele(struct user_info* pu)
{
if (pu->u_logname)
free(pu->u_logname);
free(pu);
}
int msg_cl(struct user_list*, struct user_info*, char*);
int
main(int argc, char *argv[])
{
struct user_list ul, *pl;
struct user_info *pu, *pi, *next;
struct sockaddr_in server_addr, client_addr;
fd_set r_set;
socklen_t client_len;
int server_fd, client_fd, maxfd;
in_port_t port;
int n, slctret;
char buf[MAXSIZE];
char c, *p;
if (argc != 1 && argc != 2) {
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
exit(1);
}
// 对于服务器套接字端口信息的初始化
if (argc == 1) {
server_addr.sin_port = htons(DEF_PORT);
} else if (argc == 2) {
c = 0;
for (p = argv[1]; *p != '\0'; p++) {
if (*p > '9' || *p < '0') {
c = 1;
break;
}
}
port = atoi(argv[1]);
if (c == 1) {
fprintf(stderr, "%s: %s: Port format error\n", argv[0], argv[1]);
exit(1);
}
if ((server_addr.sin_port = htons(port)) == 0) {
fprintf(stderr, "%s: %s: Zero port can't use\n", argv[0], argv[1]);
exit(1);
}
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
port = server_addr.sin_port;
// 初始服务器套接字
if ((server_fd = socket(server_addr.sin_family, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "%s: socket create error: %s\n", argv[0], strerror(errno));
exit(1);
}
// 绑定监听地址端口
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
fprintf(stderr, "%s: bind port %d error: %s\n", argv[0], ntohs(port), strerror(errno));
exit(2);
}
//侦听
if (listen(server_fd, DEF_LISTEN) < 0) {
fprintf(stderr, "%s: listen error: %s\n", argv[0], strerror(errno));
exit(3);
}
// 初始化用户表
ul_init(&ul);
pl = &ul;
pargv = (const char **)argv;
client_len = sizeof(struct sockaddr);
printf("%s: listen: *.*.*.*:%d%s\n", argv[0], ntohs(port),
ntohs(port) == DEF_PORT ? " <default>" : "");
for (;;) {
FD_ZERO(&r_set);
FD_SET(server_fd, &r_set);
maxfd = server_fd;
for (pi = pl->l_head; pi != 0; ) {
if (pi->u_fd != -1) {
FD_SET(pi->u_fd, &r_set);
maxfd < pi->u_fd ? (maxfd = pi->u_fd) : 0;
}
pi = pi->u_next;
}
if ((slctret = select(maxfd+1, &r_set, 0, 0, 0)) < 0) {
fprintf(stderr, "%s: select error: %s\n", argv[0], strerror(errno));
exit(1);
} else if (slctret == 0) {
continue;
}
/* get a new connect call accept */
if (FD_ISSET(server_fd, &r_set)) {
if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) {
fprintf(stderr, "%s: accept error: %s\n", argv[0], strerror(errno));
exit(4);
}
if (pl->l_count >= MAX_COUNT) {
/* 告诉用户服务器忙 */
n = snprintf(buf, sizeof(buf), "E::server is busy, please later try again");
/**/
printf("%s: warning: server saturated\n", argv[0]); //服务器饱和
fcntl(client_fd, F_SETFL, O_NONBLOCK);
write(client_fd, buf, n);
close(client_fd);
continue;
}
if ((pu = ui_alloc()) == 0) {
snprintf(buf, MAXSIZE, "connect refused, system memory error");
fcntl(client_fd, F_SETFL, O_NONBLOCK);
write(client_fd, buf, strlen(buf));
close(client_fd);
if (pu != 0) ui_rele(pu);
continue;
}
pu->u_fd = client_fd;
pu->u_addr = client_addr;
ui_append(pl, pu);
} /* end get a new connect */
for (pi = pl->l_head; pi != 0; pi = pi->u_next) {
if (pi->u_fd != -1) {
if (FD_ISSET(pi->u_fd, &r_set)) {
memset(buf, 0, MAXSIZE);
if ((n = read(pi->u_fd, buf, MAXSIZE)) < 0) {
fprintf(stderr, "%s: read error: %s\n", argv[0], strerror(errno));
exit(1);
} else if (n == 0) {
pi->u_crflag = 1;
if (pi->u_logname != 0) {
printf("%s: logout: %s\n", argv[0], pi->u_logname);
}
} else {
/* 开始处理通信数据 */
if (msg_cl(pl, pi, buf) < 0) {
/* error */
pi->u_crflag = 1;
continue;
}
}
}
}
}
for(pi = pl->l_head; pi; ) {
next = pi->u_next;
if (pi->u_crflag) {
ui_remove(pl, pi);
ui_rele(pi);
}
pi = next;
}
}
exit(0);
}
/* main end */
/* 对数据的简单分解,返回0指示了消息格式的不正确,为防止攻击服务器将关闭连接 */
void *
msg_fj(char* pm, char **pptype, char** ppname, char** ppmsg)
{
int n;
char *p = pm;
*pptype = pm;
n = strcspn(pm, ":");
if (n == strlen(pm)) {
goto _err_quit;
}
pm += n;
*pm = 0;
pm++;
*ppname = pm;
n = strcspn(pm, ":");
if (n == strlen(pm)) {
goto _err_quit;
}
pm += n;
*pm = 0;
pm++;
*ppmsg = pm;
return(p);
_err_quit:
*pptype = 0;
*ppname = 0;
*ppmsg = 0;
return(0);
}
/* 对分解之后的数据进行检测其合法性 */
int
msg_jc(char *ptype, char* pname, char* pmsg)
{
int n;
if (!ptype || !pname || !pmsg) {
return (-1);
}
/* 类型检测 */
switch (ptype[0]) {
case LOGIN:
case SEND:
break;
default:
return(-1);
}
/* 名字检查 */
n = strlen(pname);
if (n > MAXNAME-1 || n < MINNAME || strcspn(pname, EVFS) < n) {
return(-1);
}
return(0);
}
int
msg_cl(struct user_list* pl, struct user_info* pi, char* pm)
{
struct user_info* pto;
char type, *ptype, *pname, *pmsg;
char buf[MAXSIZE];
int n;
char *err_msg_data = "error: exception datas";
char *err_msg_rlogin = "error: re-login";
char *err_msg_nlogin = "error: no-login";
char *err_msg_unuser = "error: user no exists";
if (msg_fj(pm, &ptype, &pname, &pmsg) == 0 || msg_jc(ptype, pname, pmsg) < 0) {
printf("%s: warning: receiving exception datas\n", pargv[0]);
n = snprintf(buf, sizeof(buf), "E::%s", err_msg_data);
write(pi->u_fd, buf, n);
goto _err_quit;
}
type = *ptype;
switch (type) {
case LOGIN:
if (pi->u_logname == 0) {
if (ui_find(pl, pname) != 0) {
n = snprintf(buf, MAXSIZE, "E:%s:user exists\n", pname);
write(pi->u_fd, buf, n);
goto _err_quit;
}
n = strlen(pname);
pi->u_logname = malloc(n + 1);
strcpy(pi->u_logname, pname);
n = snprintf(buf, MAXSIZE, "L:%s:login success", pname);
write(pi->u_fd, buf, n);
printf("%s: login: %s\n", pargv[0], pname);
} else {
printf("%s: warning: %s %s\n", pargv[0], pi->u_logname, err_msg_rlogin);
write(pi->u_fd, err_msg_rlogin, strlen(err_msg_rlogin));
goto _err_quit;
}
break;
case SEND:
if (pi->u_logname == 0) {
printf("%s: warning: from (null) user send to %s's a message\n", pargv[0], pname);
n = snprintf(buf, sizeof(buf), "E:%s:%s", pname, err_msg_nlogin);
write(pi->u_fd, err_msg_nlogin, strlen(err_msg_nlogin));
goto _err_quit;
}
pto = ui_find(pl, pname);
if (pto == 0) {
n = snprintf(buf, sizeof(buf), "E:%s:%s %s", pname, err_msg_unuser, pname);
write(pi->u_fd, buf, n);
} else {
memset(buf, 0, MAXSIZE);
n = snprintf(buf, MAXSIZE, "%c:%s:%s", SEND, pi->u_logname, pmsg);
write(pto->u_fd, buf, n);
}
break;
default:
printf("%s: warning: receiving exception datas\n", pargv[0]);
goto _err_quit;
break;
}
return(0);
_err_quit:
ui_remove(pl, pi);
ui_rele(pi);
return(-1);
}
|