聊天室项目(0)-问题

一. 密码加密

因为在linux下没有getch()函数,所以就实现了一个在linux下的getch()
实现的思路(参考了网上代码):
   总体来说就是设置终端的属性
   设置为原始模式,这种模式下输入就是无缓冲的,设置过去,输入完之后然后再更改回来
   主要就是两个函数 tcgetattr()和tcsetattr()

  • tcgetattr和tcsetattr说明

int tcgetattr(int fd, struct termios termptr);/ 获取终端属性*/
int tcsetattr(int fd, int opt, const struct termios termptr);/ 设置终端属性*/

实现代码:

#include <termios.h>

//linux下没有getch()
void getch(char *buf)
{
    struct termios tm, n_tm;
    char ch;
    tcgetattr(0, &tm);  //获取当前的终端属性设置,并保存到tm结构体中
    n_tm = tm;
    n_tm.c_lflag &= ~(ICANON | ECHO);
    int  i = 0;
    while(1)
    {
        tcsetattr(0, TCSANOW, &n_tm);  //设置上更改之后的设置
        ch = getchar();
        tcsetattr(0, TCSANOW, &tm);  //接收字符完毕后将终端设置回原来的属性
        if(ch=='\n')
        {
            buf[i]='\0';
            break;
        }
        buf[i]=ch;
        i++;
        printf("*");
    }
    printf("\n");
}
二. 构建一个epoll框架

在写聊天室之前刚看过epoll的io多路复用,其实还是有点迷的,大概摸清楚了,但又好像不太懂,写聊天室时也是看了网上的epoll简单框架

代码如下:

# define MAXEPOLL 1000

epfd = epoll_create(MAXEPOLL);

ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sock_fd;

if(epoll_ctl(epfd, EPOLL_CTL_ADD, sock_fd, &ev) < 0) 
   {
    perror("epoll_ctl");
}
   printf("服务器启动成功--**--\n");

   while(1) 
   {
 	if((nfds = epoll_wait(epfd, events, MAXEPOLL, -1)) < 0)
       {
 		perror("epoll_wait");
    }

       for (i = 0; i < nfds; i++) 
       { 
           if (events[i].data.fd == sock_fd)                  
           //如果是主socket的事件的话,则表示
           //有新连接进入了,进行新连接的处理。
           {
               if ((conn_fd = accept(sock_fd, (struct sockaddr*)&cli, &socklen)) < 0) 
               {
                   perror("accept");
               }
               printf("connect success!\n套接字编号:%d\n", conn_fd);
               
               ev.events = EPOLLIN | EPOLLET;
               ev.data.fd = conn_fd;

               if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev) < 0) 
               {
                   perror("epoll_ctl");
               }
               continue;
           } 
           else if(events[i].events & EPOLLERR) 
           {
               epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,0);    
               close(events[i].data.fd);
			continue;
           } else if (events[i].events & EPOLLHUP) {
			epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,0);    
               close(events[i].data.fd);
			continue;
		} else if (events[i].events & EPOLLIN) {     //表示对应的文件描述符可以读
               //如果是已经连接的用户,并且收到数据,
               //那么进行读入
                   memset(&recv_pack, 0, sizeof(PACK));
                   if (recv(events[i].data.fd, &recv_pack, sizeof(PACK), MSG_WAITALL) < 0) 
                   {
                       close(events[i].data.fd);
                       perror("recv");
                       continue;
                   }
                   printf("\n\e[1;34m*************PACK*************\e[0m\n");
                   printf("\e[1;34m*\e[0m type           : %d\n",recv_pack.type);
                   printf("\e[1;34m*\e[0m send_fd        : %d\n", recv_pack.data.send_fd);
                   printf("\e[1;34m*\e[0m send_account   : %d\n",recv_pack.data.send_id);
                   printf("\e[1;34m*\e[0m recv_fd        : %d\n",recv_pack.data.recv_fd);
                   printf("\e[1;34m*\e[0m recv_account   : %d\n",recv_pack.data.recv_id);
                   printf("\e[1;34m*\e[0m content_buff   : %s\n",recv_pack.data.content_buff);
                   // printf("\e[1;34m*\e[0m mess_buff      : %s\n",recv_pack.data.mess_buff);
                   printf("\e[1;34m*\e[0m recv_user      : %s\n",recv_pack.data.recv_user);
                   printf("\e[1;34m*\e[0m send_name      : %s\n",recv_pack.data.send_user);
                   printf("\e[1;34m*******************************\e[0m\n");
                   recv_pack.data.recv_fd = events[i].data.fd;
                   pack = (PACK*)malloc(sizeof(PACK));
                   memcpy(pack, &recv_pack, sizeof(PACK));
                   pthread_create(&pid, NULL, solve, (void*)pack);    //开线程解决事件
               }
       }
   } 

这篇博客不错,推荐一下
epoll原理详解及epoll反应堆模型

三. MYSQL数据库的使用

也是在写聊天室之前才接触到这个mysql数据库,很多东西都还不了解,参考了大三学长的两篇博客,写的非常详细,非常受用
  mysql数据库的简单用法
  C语言操作MYSQL数据库
没有在聊天室用到的主键外键,后来自己也去了解了一些,会发现有主键和外键在操作数据库方面更方便

四. 服务器和客户端通信

这个在暑假前的任务网络编程那里看到过,服务端和客户端的交互
大体C/S模型
在这里插入图片描述先从服务器端说起:
服务器端先初始化socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。
在这时如果有个客户端初始化一个socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。
客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

五. 每个用户/群的账号生成

当时在写注册的时候一直在想怎么能生成不重复的用户账号呢,后来想到了随机数rand(),但这个有可能会生成重复的数,就又了解到了srand()随机数的生成

注册生成个人账号代码如下:

# include <time.h>

int reg(PACK *pack, MYSQL n_mysql) 
{
    MYSQL mysql = n_mysql;
    char handle[100];
    PACK *recv_pack = pack;
    int account;
    time_t t = time(NULL);
    
    srand(t);
    account = rand()%1000000;
    sprintf(handle, "insert into user values(%d,\"%s\",\"%s\",%d,%d)", account, recv_pack->data.send_user, recv_pack->data.content_buff, 0, recv_pack->data.recv_fd);
    recv_pack->data.send_id = account;

    time_t now;
    now = time(NULL);
    mysql_query(&mysql, handle);
    memset(handle, 0, sizeof(handle));
    sprintf(handle, "insert into password values(%d,%d)",account, now);
    mysql_query(&mysql, handle);

    return 0;
}

创建群生成群号代码如下:

# include <time.h>

int create_group(PACK *pack, MYSQL n_mysql) 
{
    MYSQL mysql = n_mysql;
    char handle[100];
    PACK *recv_pack = pack;
    int account;
    time_t t = time(NULL);

    srand(t);
    account = rand()%100000;
    sprintf(handle, "insert into groups values(%d,\"%s\",1)", account, recv_pack->data.recv_user);
    recv_pack->data.recv_id = account;
    mysql_query(&mysql, handle);
    memset(handle, 0, sizeof(handle));
    sprintf(handle, "insert into group_mess values(%d,\"%s\",%d,\"%s\",1)",recv_pack->data.recv_id, recv_pack->data.recv_user, recv_pack->data.send_id, recv_pack->data.send_user);
    mysql_query(&mysql, handle);

    return 0;
}
六. 收包

我是用结构体每次定长发送,就将recv()最后的参数由0设置成了等待所有数据的MSG_WAITALL,设置成这个的原因就是让包收完再返回。

但是这里的MSG_WAITALL 也只是尽量读全,在有中断的情况下recv 还是可能会被打断,造成没有读完指定的buffer的长度。

七. 申请好友/群 时的交互

申请好友时,要判断是否在线
在线,直接发送添加申请
不在线,保存到离线消息,上线再发送对
对方同意,则操作数据添加好友信息
对方不同意,则没有任何操作【这里还是有bug,因为对方同没同意,添加方最后并不知道,只有后来看好友列表时才会知道对方有没有同意好友请求】--后续会尝试解决这个问题

申请加群时,当时就没有申请什么的,就申请直接就能进了【参考qq直接进群功能 】,当然这是很大的不足,后续会继续改进,实现申请给群主或管理,登群主管理同意后再进群

八. 传输文件

这个当时在网上看了好多博客,还是有点迷,太难了
后来参考了学长的思路:发送者先循环将文件发送给服务端保存到文件中,接收者方循环接收服务端发送的文件,保存到新的文件中

发送方要写好文件在本机的绝对路径

我是先将绝对路径下文件名解析,发送到服务端时就会保存真实的文件名(这样就可以发送不同类型的文件【.c,.txt,.jpg等等】)
再服务端向接收者发送文件,就更方便了
但发送的文件大小还是不能太大

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值