C语言基础下心跳包制作

木木陶 发布于 2012/03/20 12:17
阅读 5K+
收藏 2
C语言 服务端不做心跳包该怎么确认客户端是否在线,会做心跳包的麻烦指导一下
加载中
0
Yisen
Yisen
你就不能看看心跳包原理然后自己想想怎么做吗。。
0
韭菜根
韭菜根
哥们你也太牛了吧,请教问题时要把姿态放低
木木陶
木木陶
我有得罪你的吗,不理解你什么心态。
韭菜根
韭菜根
@木木陶 不当政委你屈才了
木木陶
木木陶
姿态放低那是肯定要的 ,但是对于那些只会指责,废话连篇的小人,我想从别人那学东西,不代表我就要做你太监,对于有些只会指点江山,大道理主义人才,建议去考公务员吧,做软件真是屈才了
汉克斯
汉克斯
人家是天生做领导的料,来coding真是太可惜了啊。
0
中山野鬼
中山野鬼

哈哈。其实做程序员也需要脸皮厚的。我也经常被人指责嘛。但咱心态好。准确说不要脸。因为我要解决问题,所以一直发心跳包。楼上几位,倒好,握手信号不对,就双方自动断线了。

回楼主,你有几个事情要做。首先我的态度。和一楼是一样的,你需要搞清楚心跳包 的原理。

其次,你如果想替换掉心跳包,则你需要把最基本的网络确认功能给实现。

第三、工程级的优化。

如果你想知道具体怎么做。这样。你先做3个小模块。

1、客户端,自动发送一段内容到指定IP的端口。不管什么内容。这里主要解决定时和发送内容数据操作(方便你以后打包增加信息),现在哪怕就是个空的。

2、网络接口,可以对IP和端口进行网络接收的。最好用最简单的协议,别自己写协议栈,这里就是A,B之间,哪怕单工(一个发 ,一个收),你做好。客户端发,服务器端收。

3、服务器端客户信息管理模块。也很简单。就是每次收到个消息自动加1也行。

上面三个模块你能实现,存在个客户端进程,按照你的定时要求,比如5秒,发送消息给服务器端,服务器在屏幕上会发现有个数字在不停的增长。至少这个数字在变,则表明客户端活着。

 

木木陶
木木陶
我的函数主要是实现客户端的信息能打包发给服务端,服务端能解析出包在服务端界面上显示客户端 的信息,我现在已经做出了通信和信息打印,但是不知道心跳包怎么去安排,对于心跳包的理论意义我理解了 可是就是不知道具体该怎么实现服务端和客户端的心跳检测机制,具体可以看我代码。
0
木木陶
木木陶

服务端====================

#include<sys/types.h>
#include<ctype.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<errno.h>
#include<sys/time.h>
#include<stdio.h>
#include<strings.h>
#include<sys/select.h>
#include<stdlib.h>
#include<pthread.h>
#include<time.h>

 

struct pack
{
  char ID[32];
  char IP[32];
  char name[32];
  char password[32];
  time_t ltime;
  struct pack *next;
};
struct client_info
{
  char ID[32];
  char name[32];
  char password[32];
  time_t t;
  struct client_info *next;
};
struct pack *Head=NULL;

void c_printf();

int main()
{
 int ret;
 pthread_t idx;
 struct sockaddr_in s_servaddr,s_cliaddr;
 int request_sock,new_sock;
 int nfound,fd,maxfd,readnumber;
 uint32_t addrlen;
 fd_set rset,set;
 char buf[1024];
 //user_line("./data/user_data");
 request_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//开始socket编程
  if(request_sock < 0 )
  {
   printf("socket error");
   return -1;
  }
 memset(&s_servaddr,0,sizeof(s_servaddr));
 s_servaddr.sin_family = AF_INET;//协议族 IPV4
 s_servaddr.sin_addr.s_addr = INADDR_ANY;//经由任何可绑定的地址 IPV4
 s_servaddr.sin_port = htons(8088);//端口
 printf("bind...\n");
        //绑定计算机IP地址和端口信息到socket上
    if(bind(request_sock,(struct sockaddr *)&s_servaddr,sizeof(s_servaddr))<0)
    {
       printf("bind error");
       return -1;
    }
 printf("listen...\n");
       //已绑定的套接口开始监听 10为规定内核为套接口排队的最大连接数
 if(listen(request_sock,10) < 0)
 {
  printf("listen error");
  return -1;
 }
 FD_ZERO(&set);//对指定文件描述符进行清空初始化,否则出问题
 FD_SET(request_sock,&set);//在文件描述符集合中增加一个新的文件描述符
 maxfd = request_sock;
 //ret = pthread_create(&idx,NULL,(void *)thread_live,NULL);
 //if(ret != 0)//成功返回0
      // {
    //  printf("creat pthread error!\n");
  //      exit(1);
    // }
 while(1)
 {
  rset = set;
  printf("select now...\n");
  sleep(3);
                 //确定一个和多个套接口的状态(可读)
  nfound = select(maxfd + 1,&rset,(fd_set *)0,(fd_set *)0,NULL);
  printf("%d\n",nfound);
   if(nfound < 0)//不修改任何描述符集
   {
    printf("select error");
    return -1;
   }
   else if(nfound == 0)//描述符被清0
   {
    printf(".");fflush(stdout);
    continue;
   }
   if(FD_ISSET(request_sock,&rset))//检测request有无被改变
   {
    printf("request_sock\n");
    addrlen = sizeof(s_cliaddr);
    printf("accept...\n");
                               //连接。。
                                 addrlen=sizeof(struct sockaddr_in);
   new_sock = accept(request_sock,(struct sockaddr*)&s_cliaddr,&addrlen);                   
    if(new_sock < 0)//错误时返回-1
    {
     printf("accept error");
     return -1;
    }
  printf("connection form host %s,port %d,socket%d\r\n",inet_ntoa(s_cliaddr.sin_addr),ntohs(s_cliaddr.sin_port),new_sock);
    FD_SET(new_sock,&set);
    if(new_sock > maxfd)
            maxfd = new_sock;
                            inet_ntoa(s_cliaddr.sin_addr);
                              FD_CLR(request_sock,&rset);//把负责监听的新我文件描述符清空
                            nfound--;
                        }
  
             struct client_info *ptr;
                      struct pack *str;
                 for(fd=0;fd<=maxfd&&nfound>0;fd++)
                 {
                     if(FD_ISSET(fd,&rset))
                     {
                        nfound--;
                       ptr=(struct client_info *)malloc(sizeof(struct client_info));
                        memset(ptr,0,sizeof(struct client_info));
                        printf("读包头\n");
                        if((readnumber=read(fd,ptr,sizeof(struct client_info)))<0)
                        {
                          printf("read error");
                           return -1;
                         }
                        if(readnumber==0)
                        {
                          printf("read over\n");
                          FD_CLR(fd,&set);
                          close(fd);
                          continue;
                         }
                     }
                        str=(struct pack *)malloc(sizeof(struct pack));
                     memset(str,0,sizeof(struct pack));
                      strcpy(str->ID,ptr->ID);
                      strcpy(str->name,ptr->name);
                      strcpy(str->password,ptr->password);
                      str->ltime=ptr->t;
                        str->next=Head;
                        Head=str;  
                }//for
               c_printf();
             // break;
             }
         // c_printf();
       // sleep(2);
           close(new_sock);
           return 0;
}

void c_printf()
{
   printf("显示在线人员信息\n");
    struct pack *p;
     p=Head;
     printf("客户ID 客户昵称 客户密码 连接时间\n");
    while(p!=NULL)
    {
       printf("99\n");
      printf("%3s,%7s,%8s,%13d\n",p->ID,p->name,p->password,p->ltime);
       p=p->next;
    }
}

      
客户#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<errno.h>
#include<sys/time.h>
#include<ctype.h>
#include<sys/select.h>
#include<pthread.h>
#include<time.h>

struct client_info
{
  char ID[32];
  char name[32];
   char password[32];
   time_t t;
   struct client_info *next;
};
static int connect_server(struct sockaddr_in c_servaddr)
{
  int chose;
  int ret;
  char buf[100]="hello word";
  int c_sockfd;
  struct hostent *server;
  if(inet_pton(AF_INET,"192.168.1.24",&c_servaddr.sin_addr.s_addr)<=0)
  {
    printf("inet_pton error");
   return -1;
  }
 if((c_sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
  {
    printf("c_sockfd error");
    return -1;
  }
  c_servaddr.sin_family=AF_INET;
  c_servaddr.sin_port=htons(8088);
     struct client_info *ptr;
      ptr=(struct client_info *)malloc(sizeof(struct client_info));
       printf("请输入ID号\n");
       scanf("%s",ptr->ID);
       getchar();
       printf("请输入昵称\n");
       scanf("%s",ptr->name);
       getchar();
       printf("请输入密码\n");
       scanf("%s",ptr->password);
      ptr-> t=time(NULL);
        getchar();
   printf("连接中发送中........\n");

   ret=connect(c_sockfd,(struct sockaddr *)&c_servaddr,sizeof(c_servaddr));

 if(ret<0)
 {
   printf("connect error\n");
   return -1;
 }
 else
 { 
    if(write(c_sockfd,ptr,sizeof(struct client_info))<0)
    {
       printf("write error");
       return -1;
    }
  }
 
 fflush(stdout);
}
int main()
{
  struct sockaddr_in c_servaddr;
  connect_server(c_servaddr);
  return 0;
}
端=================

 

0
中山野鬼
中山野鬼

楼主,心跳的目的是保持客户端和服务器端的连通。本质上,你可以在服务器或客户端任何需要互通时,重新连通网络。因此,如果你认为没有心跳的必要,你大可不加。

如果你认为有心跳的必要,你可以做一个flag变量,这个flag每个计数周期,查询一次,并且查询完毕后,清0,当任何客户端信息过来时,会设置1.其实就这么简单。只不过你的定时心跳,会定时在服务器端,将这个flag置1而已。而客户端什么时候发心跳,也可以采用同样的方式实现。建议你客户端方式也是采用循环机制实现。如下:

while (1){

   mission =  get_mission();//获取现在客户端需要的网络动作

   swtich (mission){

    case SEND_MSG:
      ...; break;//你现在的打包发送代码都在这个口进去调用。
    case ...:
      ...
    default://本此循环发现啥事没做
      send_i_liveing();//发送心跳


    }
    sleep();//睡一会,不然CPU都给你占了。

}

 

 

木木陶
木木陶
嗯 谢谢了 我懂了。看来你才是高手!讨厌只会背书说大道理的“官员口气"
0
中山野鬼
中山野鬼
上面代码只是原理一下,没有while(1)的跳出口,你实际用时需要增加。
0
中山野鬼
中山野鬼
哈哈。这里高手很多。只是我口水多点而已。不代表我是高手。
0
x
xeno
我觉得任何一个互动性的网站,里面都应该有一篇置顶的帖子<提问的艺术>.
0
拉菲一箱
拉菲一箱

引用来自“木木陶”的答案

有些人真的会说大道理,我如果能领会还要请教别人指导干什么,书上无非是说这些:当在界面上显示另外一个客户端会话(或者显示绑定在这个会话上的用户)是否在线,其实就是查询会话的最后活跃时间是否在一个固定的时间之前,例如2分钟之前。这样,一个会话要想通知服务器自己在线,就需要每隔1分钟就保证操作服务器一次。如果没什么事情可通知服务器,就发“我什么都不做”的心跳消息。但是我想知道的是具体怎么做,能理解和会做不能完全等同吧,你这样的人我看多了,就是一副自势清高,指点江山,大道理话连篇的人,动手能力可见一般,即使有能力没师德,也成不了什么大事,说白了,我看不起你,虽然现在我还是菜鸟。

吐槽一下:楼主确实太激动了 哈哈
木木陶
木木陶
今天是心情不好,在这里问过几个问题,回答的都是像在背书本,追问他具体怎么做,别人还对你指指点点,所以啦,哎!
0
君无畏
君无畏
看了前面的回复。得出结论:一般coding的人话都不多,除非是妹子
中山野鬼
中山野鬼
我是特例。技术人员说我是做技术的。市场人员说是做市场的。我就是个怪物。哈。
返回顶部
顶部