文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>通讯系统服务器端

通讯系统服务器端

时间:2009-08-25  来源:rlcpc

说明:
    该软件为自由软件,本人拥有版权,您可以自由复制,改写并且发布该软件,但是请著名版权,遵循GPL,严禁用于任何商业用途

服务器说明文档:
    版本1.1
    本服务器端写成以非守护进程方式运行,若有需要请自行添加该模块代码.
    本服务器以双向链表方式实现和维护当前在线的用户列表.
    本服务器在收到不合法数据时候会主动关闭对方连接并且在终端输出该类信息,例如接收到错误格式的数据时将视为攻击行为而主动关闭.
    本服务器在收到客户端发送的数据并且经过处理后如果出错,在严重的情况下主动关闭客户端,轻微的情况下向客户端发送警告消息.例如:假如有用户采用第三方客户端登录到服务器并且绕过用户名向其他用户发送消息,服务器将发送警告并且主动关闭该连接的客户端.
    本服务器所有接受和发送的消息均未加密,若有需要请自行添加该模块代码.
    本服务器未实现IP过滤,若有需要请自行添加模块之.
    本服务器在当前用户的连接数到达上限时将不再接受新的登录,这个上限是宏MAX_COUNT的值,为了移植性,该值不可以过大.

本人英语很差,所以有些错误或警告信息不是很准确,请多多包含
为了方便,将多个文件合并成一个文件发布,后面有打包好的归档压缩文件
   server.c

#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);
}


完整服务器代码下载:
文件: server.tar.bz2
大小: 4KB
下载: 下载
排行榜 更多 +
脱出游戏海之星球

脱出游戏海之星球

冒险解谜 下载
越狱模拟器3D中文版下载

越狱模拟器3D中文版下载

休闲益智 下载
MC大战僵尸2EXPAND版下载最新版本

MC大战僵尸2EXPAND版下载最新版本

策略塔防 下载