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()函数不再阻塞,每次直接返回
  • 相关阅读:
    sql server SSMS SQL格式化工具
    SQL Server 对象性能计数器
    sql server代理不能执行exe文件
    sql server 书签查找
    non-declaration statement outside function body错误
    golang在数据库中的模糊查询
    windows使用net use命令System error 85 has occurred. The local device name is already in use.There are open files and/or incomplete directory searches pending on the connection to 错误
    前端批量迁移NAS存储
    SVN服务之VisualSVN-Server和TortoiseSVN
    redis-5.0.5 集群部署
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/5904986.html
Copyright © 2011-2022 走看看