zoukankan      html  css  js  c++  java
  • Linux Linux程序练习十二(select实现QQ群聊)

    //头文件--helper.h
    #ifndef _vzhang
    #define _vzhang
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    #define MAX_SOCKET_NUM 1024
    #define BUF_SIZE 1024
    
    //server create socket
    int server_socket(int port);
    
    //close socket
    int close_socket(int st);
    
    //start select
    int start_select(int listen_st);
    
    //connect server
    int connect_server(char * ipaddr, int port);
    
    //thread for recv message
    void * thread_recv(void *arg);
    
    //thread for send message
    void * thread_send(void *arg);
    
    #ifdef __cplusplus
    }
    #endif
    
    
    #endif
    //辅助方法--helper.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <time.h>
    #include <fcntl.h>
    #include <sys/select.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include "helper.h"
    
    //create socket
    int create_socket()
    {
        int st = socket(AF_INET, SOCK_STREAM, 0);
        if (st < 0)
        {
            printf("create socket failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        return st;
    }
    
    //set reuseaddr
    int set_reuseaddr(int st)
    {
        int on = 1;
        if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
        {
            printf("reuseaddr failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        return 0;
    }
    
    //bind IP and port
    int bind_ip(int st, int port)
    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        //type
        addr.sin_family = AF_INET;
        //port
        addr.sin_port = htons(port);
        //address
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
            printf("bind failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        return 0;
    }
    
    //listen
    int listen_port(int st, int num)
    {
        if (listen(st, num) < 0)
        {
            printf("listen failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        return 0;
    }
    
    //server create socket
    int server_socket(int port)
    {
        int st = create_socket();
        if (st < 0)
        {
            return -1;
        }
        if (set_reuseaddr(st) < 0)
        {
            return -1;
        }
        if (bind_ip(st, port) < 0)
        {
            return -1;
        }
        if (listen_port(st, 20) < 0)
        {
            return -1;
        }
        return st;
    }
    
    //get IP address by sockaddr_in
    void sockaddr_toa(const struct sockaddr_in *addr, char * ipaddr)
    {
        unsigned char * p = (unsigned char *) &(addr->sin_addr.s_addr);
        sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
    }
    
    //accept client
    int accept_client(int st)
    {
        if (st < 0)
        {
            printf("function accept_client param not correct!
    ");
            return -1;
        }
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        socklen_t len = sizeof(addr);
        int client_st = accept(st, (struct sockaddr *) &addr, &len);
        if (client_st < 0)
        {
            printf("accept failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        char buf[30] = { 0 };
        sockaddr_toa(&addr, buf);
        printf("accept by %s
    ", buf);
        return client_st;
    }
    
    //close socket
    int close_socket(int st)
    {
        if (st < 0)
        {
            printf("function close_socket param not correct!
    ");
            return -1;
        }
        close(st);
        return 0;
    }
    
    //protect message
    int protect_message(int st, int * starr)
    {
        if (st < 0 || starr == NULL)
        {
            printf("function recv_message param not correct!
    ");
            return -1;
        }
        char buf[BUF_SIZE] = { 0 };
        int i = 0;
        int rc = recv(st, buf, sizeof(buf), 0);
        if (rc < 0)
        {
            printf("recv failed ! error message :%s
    ", strerror(errno));
            return -1;
        } else if (rc == 0)
        {
            printf("client is closed !
    ");
            return -1;
        }
        /*
         *QQ消息处理:接收到client1的消息直接发送给client2
         */
        for (; i < MAX_SOCKET_NUM; i++)
        {
            if (starr[i] != st && starr[i] != -1)
            {
                //向其他的chient发送消息
                if (send(starr[i], buf, strlen(buf), 0) < 0)
                {
                    printf("send failed ! error message :%s
    ", strerror(errno));
                    return -1;
                }
            }
        }
        return 0;
    }
    
    //send message
    int send_message(int st, int * starr)
    {
        return 0;
    }
    
    //start select
    int start_select(int listen_st)
    {
        if (listen_st < 0)
        {
            printf("function create_select param not correct!
    ");
            return -1;
        }
        int i = 0;
        //定义select第一个参数变量
        int maxfd = 0;
        int rc = 0;
        //创建客户端socket池
        int client[MAX_SOCKET_NUM] = { 0 };
        //初始化socket池
        for (; i < MAX_SOCKET_NUM; i++)
        {
            client[i] = -1;
        }
        //定义事件数组结构
        fd_set allset;
        while (1)
        {
            //清空事件数组
            FD_ZERO(&allset);
            //将服务器端socket加入事件数组
            FD_SET(listen_st, &allset);
            //假设值最大的socket是listen_st
            maxfd = listen_st;
            //将所有客户端socket加入到事件数组
            for (i = 0; i < MAX_SOCKET_NUM; i++)
            {
                if (client[i] != -1)
                {
                    FD_SET(client[i], &allset);
                    if (maxfd < client[i])
                    {
                        maxfd = client[i];
                    }
                }
            }
            //select阻塞接收消息
            rc = select(maxfd + 1, &allset, NULL, NULL, NULL);
            //select函数报错,直接退出循环
            if (rc < 0)
            {
                printf("select failed ! error message :%s
    ", strerror(errno));
                break;
            }
            if (FD_ISSET(listen_st, &allset))
            {
                rc--;
                //处理服务端socket事件
                int client_st = accept_client(listen_st);
                if (client_st < 0)
                {
                    /*
                     * 如果accept失败,直接退出select,
                     * continue不太合适,因为其他的客户端事件还没有处理,这样就会丢事件
                     */
                    break;
                }
                //将客户端socket放到socket池中
                for (i = 0; i < MAX_SOCKET_NUM; i++)
                {
                    if (client[i] == -1)
                    {
                        client[i] = client_st;
                        break;
                    }
                }
                if (i == MAX_SOCKET_NUM)
                {
                    printf("服务器端socket池已经满了");
                    //socket池已满,关闭客户端连接
                    close_socket(client_st);
                }
            }
            //处理客户端事件
            for (i = 0; i < MAX_SOCKET_NUM; i++)
            {
                //如果事件数组中的事件已经处理完成,直接进入跳出当前循环
                if (rc <= 0)
                {
                    break;
                }
                if (client[i] != -1)
                {
                    if (FD_ISSET(client[i], &allset))
                    {
                        //该socket有事件发生
                        if (protect_message(client[i], client) < 0)
                        {
                            //接收消息失败,但是由于逻辑复杂,直接退出select
                            //关闭客户端socket
                            close_socket(client[i]);
                            //将该socket从socket池中删除
                            client[i] = -1;
                            break;
                        }
                    }
                }
            }
        }
        return 0;
    }
    
    //connect server
    int connect_server(char * ipaddr, int port)
    {
        int st = create_socket();
        if (st < 0)
        {
            return -1;
        }
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        //type
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ipaddr);
        if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
            printf("connect failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        return st;
    }
    
    //thread for recv message
    void * thread_recv(void *arg)
    {
        int st = *(int *) arg;
        if (st < 0)
        {
            printf("function thread_recv param not correct!
    ");
            return NULL;
        }
        char buf[BUF_SIZE] = { 0 };
        while (1)
        {
            if (recv(st, buf, sizeof(buf), 0) < 0)
            {
                printf("recv failed ! error message :%s
    ", strerror(errno));
                break;
            }
            printf("%s", buf);
            memset(buf, 0, sizeof(buf));
        }
        return NULL;
    }
    
    //thread for send message
    void * thread_send(void *arg)
    {
        int st = *(int *) arg;
        if (st < 0)
        {
            printf("function thread_send param not correct!
    ");
            return NULL;
        }
        char buf[BUF_SIZE] = { 0 };
        while (1)
        {
            read(STDIN_FILENO, buf, sizeof(buf));
            if (send(st, buf, sizeof(buf), 0) < 0)
            {
                printf("send failed ! error message :%s
    ", strerror(errno));
                break;
            }
            memset(buf, 0, sizeof(buf));
        }
        return NULL;
    }
    //QQ客户端
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <errno.h>
    #include "helper.h"
    
    int main(int arg, char *args[])
    {
        if (arg < 3)
        {
            printf("please print two param !
    ");
            return -1;
        }
        int port = atoi(args[2]);
        char ipaddr[30] = { 0 };
        strcpy(ipaddr, args[1]);
        //connect server
        int st = connect_server(ipaddr, port);
        if (st < 0)
        {
            return -1;
        }
        //recv thread
        pthread_t thr1, thr2;
        if (pthread_create(&thr1, NULL, thread_recv, &st) != 0)
        {
            printf("pthread_create failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        if (pthread_create(&thr2, NULL, thread_send, &st) != 0)
        {
            printf("pthread_create failed ! error message :%s
    ", strerror(errno));
            return -1;
        }
        //join
        pthread_join(thr1,NULL);
        pthread_join(thr2,NULL);
        //close socket
        close_socket(st);
        return 0;
    }
    //QQ服务端
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "helper.h"
    
    int main(int arg,char *args[])
    {
        //服务器端需要传入端口号
        if(arg<2)
        {
            printf("please print one param !
    ");
            return -1;
        }
        int port=atoi(args[1]);
        int st=server_socket(port);
        if(st<0)
        {
            return -1;
        }
        //开启select 监听事件
        start_select(st);
        //close socket
        close_socket(st);
        return 0;
    }
    .SUFFIXES:.c .o
    CC=gcc
    SRCS1=QQserver.c
        helper.c
    SRCS2=QQclient.c
        helper.c
    OBJS1=$(SRCS1:.c=.o)
    OBJS2=$(SRCS2:.c=.o)
    EXEC1=mserver
    EXEC2=mclient
    
    start:$(OBJS1) $(OBJS2)
        $(CC) -o $(EXEC1) $(OBJS1)
        $(CC) -o $(EXEC2) $(OBJS2) -lpthread
        @echo "-------ok-----------"
    .c.o:
        $(CC) -Wall -g -o $@ -c $<
    clean:
        rm -f $(OBJS1)
        rm -f $(EXEC1)
        rm -f $(OBJS2)
        rm -f $(EXEC2)
    小结:
      这次程序编码调试都很快,调试中第一个错误,客户端连接不上服务器,但是connect不报错,我几乎每次写网络程序都有这个问题,就目前而言有两种出错可能
    出错场景1:服务端socket有问题,为0或者不是正确的socket
    出错场景2:服务器端的socket没有放在select池中,没有被select监视
    我暂时就只有这两种犯错场景。
    调试第二个错误:“Segmentation fault”,这个错误我觉得一般都是操作内存出了问题,内存泄漏,我这边是因为在函数sockaddr_toa()中
    unsigned char *p=(unsiged char *)&(addr->sin_addr.s_addr); //没有取addr->sin_addr.s_addr的地址转化成unsigned char类型,发生内存泄漏
    调试第三个错误是逻辑错误,每次有客户端socket接收数据出错后,我没有从socket池中将该socket清除,导致select()函数不再阻塞,每次直接返回
  • 相关阅读:
    jsp转向
    什么是 XDoclet?
    tomcat中的几点配置说明
    mysql5问题
    POJ 3734 Blocks
    POJ 2409 Let it Bead
    HDU 1171 Big Event in HDU
    POJ 3046 Ant Counting
    HDU 2082 找单词
    POJ 1286 Necklace of Beads
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/5904986.html
Copyright © 2011-2022 走看看