OICQ

局域网 OICQ 程序设计

平台开发和环境简介

Linux 系统 + Gcc + makefile

功能描述

实现局域网 OICQ 程序设计,包括客户端和服务端。
客户端描述:客户端运行开始出现登陆界面。与服务端进行连接,连接后把账号信息发送给服务端,服务端验证后,把确认结果通知客户端。如果通过验证,客户端从服务端接收其他在线客户端信息并把这些客户信息显示给用户。用户可以选择客户并与之进行信息交流。即发送消息和接受消息。并把结果显示给用户。
服务端功能描述:服务端启动后,等待客户端连接。接受客户端发送过来的账号信息。进行验证。并把验证的结果返回给客户端。如果验证通过,记录客户端的信息。并把该客户端的记录的信息发送给其他的客户端,也把其他的在线用户发送给该用户,实现整个网络内的在线客户信息的同步。

项目实施

服务端利用epoll函数实现对客户端多用户连接的管理,首先将bind、listen过的socket的文件描述符加到epoll集合中,监听它的读,每当有新的客户端连接过来,就将与之通讯的文件描述符加到epoll集合中并创建一个在线户列表记录它们的信息(我用的是链表来储存这些信息)当客户端断开后就删除epoll中的文件描述符,并把在线户列表中的信息删除(删除这个节点)。每当与他们通信的描述符“准备好了”,epoll_wait函数就会解除阻塞。客户端发送信息给服务端的信息都有一个包头,服务端通过这个包头来确定客户端发送的是什么类型的信息,以便后续的操作。
客户端有注册、登录、注销的功能,登录成功后才可以发送信息和接受信息。发送信息,服务端建立一个记录信息的链表,每一个用户发送信息,服务端都会增加一个节点记录信息(发送方的ID,接受方的ID,发送的信息),

epoll

我采用的I/O复用机制,来实现对多客户的连接,用LINUX支持的epoll来实现的,因为epoll的速度比select要快,管理文件描述的数量也别select要多。

    while(1)
    {
        nfound = epoll_wait(efd,evs,20,-1);
        if(nfound < 0)
        {
            perror("epoll_wait");
            exit(0);
        }
        for(int i=0;i<nfound;i++)
        {
            if(evs[i].data.fd == fd)//有新的客户连接
            {
                nfd = accept(fd,(struct sockaddr *)&caddr,&caddr_len);
                if(nfd < 0)
                {
                    perror("accept");
                    return -1;
                }
                struct sockaddr_in addr;
                read(nfd,&addr,sizeof(addr));

                ev.data.fd = nfd;
                ev.events = EPOLLIN;
                epoll_ctl(efd,EPOLL_CTL_ADD,nfd,&ev);

                add_link(&head,nfd,addr);// 增加节点记录客户端信息
            }
            else
            {
                ret = my_read(evs[i].data.fd,head);//read
                if (ret == -1)
                {
                    epoll_ctl(efd,EPOLL_CTL_DEL,evs[i].data.fd,&evs[i]);//删除出错的客户端fd

                    del_node(&head,evs[i].data.fd);//删除出错的节点

                }
            }
        }

心跳机制

客户端建立一个线程,传入与服务端TCP协议连接的描述符,每隔3秒发送一个心跳包给服务端,证明自己“活着”。

  void *send_heatbeat(void *ptr)
   {
       pthread_detach(pthread_self());
   
       int fd = *(int *)ptr;
       PACK *ph = NULL;
       ph = malloc(sizeof(PACK));
       ph -> type = HEAT_FLG;
       ph -> len = 0;
   
       while(1)
       {
           sleep(3);
           write(fd,ph,sizeof(PACK));
       }
   }

服务端建立一个线程,来实现对每一个客户的超时管理,如果客户端超过十秒没有发送心跳包给服务端,服务端就认为,该客户已经离线,进行离线处理。


void *admin_thread(void *ptr)
{
    pthread_detach(pthread_self());
    
    BAG pt = *(BAG *)ptr;//将在线户链表的头结点的地址和epoll的文件描述符打包传入
    NODE **head = pt.pb;
    NODE *pa = *head;
    int efd = pt.efd;
    struct epoll_event ev; 

    while(1)
    {   
        pa = *head;
        sleep(1);
        pthread_spin_lock(&spin);//加锁防止其他线程同时对在线户列表同时写
        while(pa != NULL)
        {   
            (pa -> count)--;
            if(pa -> count == 0)
            {  
                ev.data.fd = pa->nfd;
                ev.events = EPOLLIN;
                epoll_ctl(efd,EPOLL_CTL_DEL,pa->nfd,&ev);//删除长时间未响应的客户端fd
                thread_del_node(head,pa->nfd);//删除长时间未响应的节点

            }   
            
            pa = pa->next;
        }   
        pthread_spin_unlock(&spin);//解锁
    }   
}

守护进程

服务端使用守护进程,一种后台运行独立于所有终端控制之外的进程。守护进程中重定向标准输入、输出、错误输出的文件描述符到指定文件。

void deamon(void)
{
    int pid;
    int fd; 
    FILE *fp;
    int res;

    pid = fork();
    if(pid > 0)
    {   
        exit(0);
    }   
    setsid();
    umask(0022);

    res = chdir("/home/shangqian/ROOT/project/QQ/deamon");
    if(res < 0)
    {   
        perror("chdir");

        exit(-1);
    }   

    close(STDIN_FILENO);

    fd = open("/dev/null",O_WRONLY);

    dup2(fd,STDOUT_FILENO);
    dup2(fd,STDERR_FILENO);
    fp = fopen("./dea_conf","w");
    if(fp < 0)
    {   
        printf("wrong\n");
        exit(0);
    }   
    fprintf(fp,"%d\n",getpid());//记录守护进程的进程号
    fclose(fp);
}

结果展示

客户端界面
在这里插入图片描述
登录成功后界面
在这里插入图片描述
发送信息界面
在这里插入图片描述
查看在线用户
在这里插入图片描述
查看聊天记录
在这里插入图片描述

总结

开发完这个项目后,我对网络通讯方面有了更深的了解,对于tcp/ip通信协议和udp通讯协议有了全面的认识,对于socket编程也更加的熟练。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值