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\n", 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\n", 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\n", 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\n", 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!\n");
        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\n", strerror(errno));
        return -1;
    }
    char buf[30] = { 0 };
    sockaddr_toa(&addr, buf);
    printf("accept by %s\n", buf);
    return client_st;
}

//close socket
int close_socket(int st)
{
    if (st < 0)
    {
        printf("function close_socket param not correct!\n");
        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!\n");
        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\n", strerror(errno));
        return -1;
    } else if (rc == 0)
    {
        printf("client is closed !\n");
        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\n", 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!\n");
        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\n", 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\n", 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!\n");
        return NULL;
    }
    char buf[BUF_SIZE] = { 0 };
    while (1)
    {
        if (recv(st, buf, sizeof(buf), 0) < 0)
        {
            printf("recv failed ! error message :%s\n", 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!\n");
        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\n", 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 !\n");
        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\n", strerror(errno));
        return -1;
    }
    if (pthread_create(&thr2, NULL, thread_send, &st) != 0)
    {
        printf("pthread_create failed ! error message :%s\n", 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 !\n");
        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 [email protected] -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()函数不再阻塞,每次直接返回
时间: 2024-10-12 08:07:18

Linux Linux程序练习十二(select实现QQ群聊)的相关文章

Linux Shell常用技巧(十二) Shell编程

Linux Shell常用技巧(十二) Shell编程 二十三. Bash Shell编程:  1.  读取用户变量:    read命令是用于从终端或者文件中读取输入的内建命令,read命令读取整行输入,每行末尾的换行符不被读入.在read命令后面,如果没有指定变量名,读取的数据将被自动赋值给特定的变量REPLY.下面的列表给出了read命令的常用方式: 命令格式 描述 read answer 从标准输入读取输入并赋值给变量answer. read first last 从标准输入读取输入到第

Linux Shell常用技巧(十二)

二十三. Bash Shell编程:  1.  读取用户变量:    read命令是用于从终端或者文件中读取输入的内建命令,read命令读取整行输入,每行末尾的换行符不被读入.在read命令后面,如果没有指定变量名,读取的数据将被自动赋值给特定的变量REPLY.下面的列表给出了read命令的常用方式: 命令格式 描述 read answer 从标准输入读取输入并赋值给变量answer. read first last 从标准输入读取输入到第一个空格或者回车,将输入的第一个单词放到变量first中

Linux与云计算——第二阶段Linux服务器架设 第一十二章:数据库搭建—PostgreSQL

Linux与云计算--第二阶段Linux服务器架设 第一十二章:数据库搭建-PostgreSQL 1.1 安装PostgreSQL [1] 安装并启动PostgreSQL. [[email protected] ~]# yum -y install postgresql-server [[email protected] ~]# postgresql-setup initdb Initializing database ... OK [[email protected] ~]# vim /var

嵌入式Linux裸机开发(十二)——iNand简介

嵌入式Linux裸机开发(十二)--iNand简介 一.iNand简介 iNand是SanDisk公司研发的存储芯片,可以看成SD卡或MMC卡芯片化. iNand是SanDisk公司符合eMMC协议的芯片系列名称,内部采用MLC存储颗粒.iNand接口电路设计复杂,功能完善,提供eMMC接口协议,与SoC的eMMC控制器配对通信. 相对MLC NandFlash,iNAND有以下优点: 1.提高性能 A.减少SOC的工作量,节约SOC资源. 如果使用MLC做存储,SOC要参与FLASH的坏块管理

Linux常用命令(十二)日志文件分析

? ? ? ? ? ? ? ? ? ? ? ? Linux常用命令(十二)日志文件分析 日志文件是用于记录Linux系统中各种运行消息的文件,相当于Linux主机的"日记".不同的日志文件记载了不同类型的信息,如Linux内核消息.用户登录事件.程序错误等. 一.主要日志文件 ???? ? ? ? 在Linux系统中,日志数据主要包括以下三种类型. ■ 内核及系统日志: 这种日志数据由系统服务rslslog统一管理,根据其主配置文件/etc/rsyslog.conf中的设置决定将内核消

嵌入式 Linux C语言(十二)——单链表

嵌入式 Linux C语言(十二)--单链表 一.单链表简介 1.单链表的结构 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素. 链表中的数据是以节点来表示的,每个节点由两部分构成:一个是数据域,存储数据值,另一个是指针域,存储指向下一个节点的指针. 2.单链表的节点 单链表节点的数据结构如下: typedef struct data { unsigned int id;//学生身份ID char name[LENGTH];//学生姓名 char subject[

马哥学习笔记二十二——高可用集群原理

HA Resource:资源 FailOver:故障转移 FailBack:故障转回 资源粘性:资源是否倾向于留在当前节点 Messaging Layer:集群服务信息层,基于UDP互相传递心跳信息,集群事务信息等 heartbeat(v1,v2,v3) heartbeat v3:heartbeat,pacemaker,cluster-glue corosync cman keepalived ultramonkey CRM:(cluster resource manager)集群资源管理器,统

Linux学习之CentOS(十二)----认识ext文件系统(转)

认识ext文件系统 硬盘组成与分割 文件系统特性 Linux 的 EXT2 文件系统(inode) 与目录树的关系 EXT2/EXT3 文件的存取与日志式文件系统的功能 Linux 文件系统的运行 挂载点的意义 其他 Linux 支持的文件系统与 VFS 认识 EXT2 文件系统 Linux最传统的磁盘文件系统(filesystem)使用的是EXT2这个啦!所以要了解文件系统就得要由认识EXT2开始! 而文件系统是创建在硬盘上面的,因此我们得了解硬盘的物理组成才行,所以底下只会很快的复习这两部份

Linux学习之CentOS(十二)------磁盘的分区、格式化、挂载(转)

磁盘分区.格式化.挂载磁盘分区    新增分区    查询分区    删除分区磁盘格式化    mkfs    mke2fs磁盘挂载与卸载    mount    umount 磁盘的分区.格式化.挂载 对于一个系统管理者( root )而言,磁盘的的管理是相当重要的一环,尤其近来硬盘已经渐渐的被当成是消耗品了 ..... 如果我们想要在系统里面新增一颗硬盘时,应该有哪些动作需要做的呢: 对磁盘进行分区,以创建可用的 partition : 对该 partition 进行格式化( format