C 实现HTML5服务时,遇到握手状态的判断问题...

色郎中 2015-04-09 01:49:19
下面是服务器接受函数的代码:

如果是某个客户端第一次连接服务器,则建立握手先,以后,该连接除非重新连接服务器再,则不需要握手动作,做普通SOCKET通信

但是,下面代码作判断时,服务器只能与一个客户端建立连接,其他客户端就不能再连接上了,服务建立握手...

当多个客户端存在时,如何判断每一个客户端是否是第一次与服务器连接,以便建立握手?


// receive data
void RecvData(int fd, int events, void *arg)
{
unsigned char ret_len;
unsigned char send_str[254];
struct myevent_s *ev = (struct myevent_s*)arg;
int len,i;

memset(ev->buff,0,REQUEST_LEN_MAX);
len = recv(fd, ev->buff, sizeof(ev->buff), 0);
//printf("---------------------\n");
printf("recv data len: %d\n",len);
//printf("---------------------\n");
//printf("recv data content: %s\n",ev->buff);
printf("\n");
if(len<480){
//printf("recv data content:\n");
for(i=0;i<len;i++)
printf("%02x ",ev->buff[i]);
}

printf("\n");
if((0==connected))// 目前只能接受一个客户端的连接握手,当第二个客户端请求连接时,就走不到握手协议这里,也就无法建立连接....
{
printf(" shakehand is ok %d \n",fd);
//printf("read:%d\n%s\n",len,ev->buff);
secWebSocketKey=computeAcceptKey(ev->buff);
shakeHand(fd,secWebSocketKey);
connected=1;
//printf("22222 connected is :%d \n",connected);
//continue;
}
/*
data=analyData(ev->buff,len);
printf("data conent: %s \n",ev->buff);
printf("data len: %d \n",len);
response(fd,data);
*/
if(len<254){
ret_len=analyData_2(ev->buff,len,send_str);

//printf("254 content:\n");
//for(i=0;i<ret_len;i++)
//printf("%02x ",send_str[i]);
}
if(len==6 & ev->buff[0]==0x88)//页面上断开连接
{
//printf("3333 connected is :%d \n",connected);
connected=0;
close(ev->fd);
printf("[fd=%d] pos[%d], closed gracefully.\n", fd, ev-g_Events);
//printf("4444 connected is :%d \n",connected);
}
if(len > 0)
{
ev->buff[0]=0x81;
ev->buff[1]=12;
ev->buff[2]=0x46;
ev->buff[3]=0x30;
ev->buff[4]=0x30;
ev->buff[5]=0x36;
ev->buff[6]=0x45;
ev->buff[7]=0x45;
ev->buff[8]=0x30;
ev->buff[9]=0x31;
ev->buff[10]=0x32;
ev->buff[11]=0x33;
ev->buff[12]=0x42;
ev->buff[13]=0x43;
ev->buff[14]=0x42;
ev->buff[15]=0x43;

ev->len += len;
printf("\n");
//ev->buff[0]=buffer[0];
//ev->buff[1]=buffer[1];
//memcpy(ev->buff,buffer,buffer[1]+2);
ev->len=14;
ev->call_back = SendData;
// change to send event
//EventSet(ev, fd, SendData, ev);
EventAdd(g_epollFd, EPOLLOUT, ev);
}
else if(len == 0)
{
close(ev->fd);
printf("[fd=%d] pos[%d], closed gracefully.\n", fd, ev-g_Events);
}
else
{
close(ev->fd);
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
}
}

...全文
183 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
色郎中 2015-04-10
  • 打赏
  • 举报
回复
引用 5 楼 dooX8086 的回复:
题外话: 看看 RecvData 这个函数 每当 socket 可读时就触发一次, 每次都要判断一次 "握手了没有呀"... "握手了没有呀" ... 用 FSM 的方式就可以 减少一次 if , 还可以务业分离,把 握手 业务抽出来, 而且 H5的握手协议 是很很多版本 - -!!! // ===================================== 好久没碰过 epoll 了.... 楼主可以试试 libevent 或 libev
早上试试了, 直接在结构体了加 一个状态变量,可以的,多个客户端连接没有问题。通信都正常也... 你的建议非常好,看你的描述,应该是FSM更好,更合理。 但是你第一回复,没懂... 也不好意思老麻烦你
色郎中 2015-04-09
  • 打赏
  • 举报
回复
引用 5 楼 dooX8086 的回复:
题外话: 看看 RecvData 这个函数 每当 socket 可读时就触发一次, 每次都要判断一次 "握手了没有呀"... "握手了没有呀" ... 用 FSM 的方式就可以 减少一次 if , 还可以务业分离,把 握手 业务抽出来, 而且 H5的握手协议 是很很多版本 - -!!! // ===================================== 好久没碰过 epoll 了.... 楼主可以试试 libevent 或 libev
谢谢你呀 俺有空去了解一下libevent ,,, 如果按照您4楼的方法在 设置一个状态,,那在发送函数里,会不会改变这个值的状态呢? 按你方法先试试,有问题再请教


// send data  
void SendData(int fd, int events, void *arg)  
{  
    struct myevent_s *ev = (struct myevent_s*)arg;  
    int len,i;  
    // send data  
    len = send(fd, ev->buff + ev->s_offset, ev->len - ev->s_offset, 0);
    if(len > 0)
    {
        //printf("send[fd=%d], [%d<->%d]%s\n", fd, len, ev->len, ev->buff);
        printf("send data is :\n");
        for(i=0;i<ev->buff[1]+2;i++)
                printf("  %02X", ev->buff[i]); 
        printf("\n");
        ev->s_offset += len;
        if(ev->s_offset == ev->len)
        {
            // change to receive event
            EventDel(g_epollFd, ev);
            EventSet(ev, fd, RecvData, ev);  
            EventAdd(g_epollFd, EPOLLIN, ev);  
        }   //这三个函数都操作到了EV这个结构提,而状态都会经过这三个函数走一圈
    }
    else  
    {  
        close(ev->fd);  
        EventDel(g_epollFd, ev);  
        printf("send[fd=%d] error[%d]\n", fd, errno);  
    }  
}  
dooX8086 2015-04-09
  • 打赏
  • 举报
回复
题外话: 看看 RecvData 这个函数 每当 socket 可读时就触发一次, 每次都要判断一次 "握手了没有呀"... "握手了没有呀" ... 用 FSM 的方式就可以 减少一次 if , 还可以务业分离,把 握手 业务抽出来, 而且 H5的握手协议 是很很多版本 - -!!! // ===================================== 好久没碰过 epoll 了.... 楼主可以试试 libevent 或 libev
dooX8086 2015-04-09
  • 打赏
  • 举报
回复
引用 1 楼 wanyeye 的回复:
相对全一点的代码,代码都是来自网络,目前这样方式,只能接受一个客户端连接,并交互通信。 如何在客户端建立连接时,为每个客户端,标记一个状态,如果是第一次连接,则标记一个状态,以便完成握手动作, 握手后,修改状态,直到该客户端关闭连接再恢复该状态,,方便该客户端下次连接服务器以完成握手。 ......
每一个客户端都是一个 struct myevent_s ,如果用状态标记这种方式可以向里面添加

struct myevent_s  
{  
    int fd;  
    void (*call_back)(int fd, int events, void *arg);  
    int events;  
    void *arg;  
    int status; // 1: in epoll wait list, 0 not in  
    unsigned char buff[1024]; // recv data buffer  
    int len, s_offset;  
    long last_active; // last active time

   int connected;    //添加这个标记-- 0:未握手.1:完成握手.
}; 


//
//  然后修改
//
void RecvData(int fd, int events, void *arg)  
{
    // ..
   // ..
  // ..
  // if((0==connected))/目前只能接受一个客户端的连接握手,当第二个.......
   if((0 == ev->connected))  ///
    {             
                printf(" shakehand is ok %d \n",fd);
                // ...
             secWebSocketKey=computeAcceptKey(ev->buff);   
            shakeHand(fd,secWebSocketKey);

              // connected=1;     // ==========> 相应的 connected 都改成 ev->connected;
            ev->connected=1;

           // ....
    }
}
色郎中 2015-04-09
  • 打赏
  • 举报
回复
引用 2 楼 dooX8086 的回复:
觉得做个类 FSM 的操作 会方便点 添加一个 握手状态处理 void hand_shake(int fd, int evs, void *arg) { /// 握手处理完成后 // 改变 call_back; // EventSet( ...) 域直接 ((struct myevent_s *) arg)->call_back = RecvData; return 0; } // 修改接受 客户端connect函数 void AcceptConn(int fd, int events, void *arg) { // add a read event for receive data // EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]); // 把原来的 RecvData 改成 hand_shake EventSet(&g_Events[i], nfd, hand_shake, &g_Events[i]); } // ====================================== 把 myevent_s 中的 call_back 变成 call_backs 这种方法会更完整点 调用: ev->call_back[ev->status](ev->fd, events[i].events, ev->arg); 这样改变 status 就改变了 call_back
没看懂。。。
dooX8086 2015-04-09
  • 打赏
  • 举报
回复
觉得做个类 FSM 的操作 会方便点 添加一个 握手状态处理 void hand_shake(int fd, int evs, void *arg) { /// 握手处理完成后 // 改变 call_back; // EventSet( ...) 域直接 ((struct myevent_s *) arg)->call_back = RecvData; return 0; } // 修改接受 客户端connect函数 void AcceptConn(int fd, int events, void *arg) { // add a read event for receive data // EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]); // 把原来的 RecvData 改成 hand_shake EventSet(&g_Events[i], nfd, hand_shake, &g_Events[i]); } // ====================================== 把 myevent_s 中的 call_back 变成 call_backs 这种方法会更完整点 调用: ev->call_back[ev->status](ev->fd, events[i].events, ev->arg); 这样改变 status 就改变了 call_back
色郎中 2015-04-09
  • 打赏
  • 举报
回复
相对全一点的代码,代码都是来自网络,目前这样方式,只能接受一个客户端连接,并交互通信。 如何在客户端建立连接时,为每个客户端,标记一个状态,如果是第一次连接,则标记一个状态,以便完成握手动作, 握手后,修改状态,直到该客户端关闭连接再恢复该状态,,方便该客户端下次连接服务器以完成握手。


#define REQUEST_LEN_MAX 1024
#define DEFEULT_SERVER_PORT 8000
#define WEB_SOCKET_KEY_LEN_MAX 256
#define RESPONSE_HEADER_LEN_MAX 1024
#define LINE_MAX 256
#define HOST_IP_ADDRESS_TCP "192.168.34.52"
#define MAX_EVENTS 500  
#define HOST_PORT_TCP 4530

int connected=0;//0:not connect.1:connected.

struct myevent_s  
{  
    int fd;  
    void (*call_back)(int fd, int events, void *arg);  
    int events;  
    void *arg;  
    int status; // 1: in epoll wait list, 0 not in  
    unsigned char buff[1024]; // recv data buffer  
    int len, s_offset;  
    long last_active; // last active time
};  
// set event  
void EventSet(struct myevent_s *ev, int fd, void (*call_back)(int, int, void*), void *arg)  
{  
    ev->fd = fd;  
    ev->call_back = call_back;  
    ev->events = 0;  
    ev->arg = arg; 
    ev->status = 0;
    bzero(ev->buff, sizeof(ev->buff));
    ev->s_offset = 0;  
    ev->len = 0;
    ev->last_active = time(NULL);  
}  
// add/mod an event to epoll  
void EventAdd(int epollFd, int events, struct myevent_s *ev)  
{  
    struct epoll_event epv = {0, {0}};  
    int op;  
    epv.data.ptr = ev;  
    epv.events = ev->events = events;  
    if(ev->status == 1){  
        op = EPOLL_CTL_MOD;  
    }  
    else{
        op = EPOLL_CTL_ADD;  
        ev->status = 1; 
    }  
    if(epoll_ctl(epollFd, op, ev->fd, &epv) < 0)  
        printf("Event Add failed[fd=%d], evnets[%d]\n", ev->fd, events);  
    else  
        printf("Event Add OK[fd=%d], op=%d, evnets[%0X], status:[%d]\n", ev->fd, op, events, ev->status);  
}  
// delete an event from epoll  
void EventDel(int epollFd, struct myevent_s *ev)  
{  
    struct epoll_event epv = {0, {0}};  
    if(ev->status != 1) return;  
    epv.data.ptr = ev;  
    ev->status = 0;
    epoll_ctl(epollFd, EPOLL_CTL_DEL, ev->fd, &epv);  
}  
int g_epollFd;  
struct myevent_s g_Events[MAX_EVENTS+1]; // g_Events[MAX_EVENTS] is used by listen fd  
void RecvData(int fd, int events, void *arg);  
void SendData(int fd, int events, void *arg);  

// accept new connections from clients  
void AcceptConn(int fd, int events, void *arg)  
{  
    struct sockaddr_in sin;  
    socklen_t len = sizeof(struct sockaddr_in);  
    int nfd, i;  
    // accept  
    if((nfd = accept(fd, (struct sockaddr*)&sin, &len)) == -1)  
    {  
        if(errno != EAGAIN && errno != EINTR)  
        {  
        }
        printf("%s: accept, %d", __func__, errno);  
        return;  
    }  
    do  
    {  
        for(i = 0; i < MAX_EVENTS; i++)  
        {  
            if(g_Events[i].status == 0)  
            {  
                break;  
            }  
        }  
        if(i == MAX_EVENTS)  
        {  
            printf("%s:max connection limit[%d].", __func__, MAX_EVENTS);  
            break;  
        }  
        // set nonblocking
        int iret = 0;
        if((iret = fcntl(nfd, F_SETFL, O_NONBLOCK)) < 0)
        {
            printf("%s: fcntl nonblocking failed:%d", __func__, iret);
            break;
        }
        // add a read event for receive data  
        EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]);  
        EventAdd(g_epollFd, EPOLLIN, &g_Events[i]); 
    }while(0);  
    
    printf("new conn %s, %d,time:%ld, pos:%d \n", inet_ntoa(sin.sin_addr), 
            ntohs(sin.sin_port), g_Events[i].last_active, i); 
}  
// receive data  
// 帖子内容大小有限制,放主贴里了

// send data  
void SendData(int fd, int events, void *arg)  
{  
    struct myevent_s *ev = (struct myevent_s*)arg;  
    int len,i;  
    // send data  
    len = send(fd, ev->buff + ev->s_offset, ev->len - ev->s_offset, 0);
    if(len > 0)
    {
        //printf("send[fd=%d], [%d<->%d]%s\n", fd, len, ev->len, ev->buff);
        printf("send data is :\n");
        for(i=0;i<ev->buff[1]+2;i++)
                printf("  %02X", ev->buff[i]); 
        printf("\n");
        ev->s_offset += len;
        if(ev->s_offset == ev->len)
        {
            // change to receive event
            EventDel(g_epollFd, ev);
            EventSet(ev, fd, RecvData, ev);  
            EventAdd(g_epollFd, EPOLLIN, ev);  
        }
    }
    else  
    {  
        close(ev->fd);  
        EventDel(g_epollFd, ev);  
        printf("send[fd=%d] error[%d]\n", fd, errno);  
    }  
}  

void InitListenSocket(int epollFd, short port,char* ip)  
{  
    int listenFd = socket(AF_INET, SOCK_STREAM, 0);  
    fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking  
    printf("server listen fd=%d\n", listenFd);  
    EventSet(&g_Events[MAX_EVENTS], listenFd, AcceptConn, &g_Events[MAX_EVENTS]);  
    // add listen socket  
    EventAdd(epollFd, EPOLLIN, &g_Events[MAX_EVENTS]);  
    // bind & listen  
    struct sockaddr_in sin;  
    bzero(&sin, sizeof(sin));  
    sin.sin_family = AF_INET;  
    //sin.sin_addr.s_addr = INADDR_ANY;  
    //sin.sin_addr.s_addr = inet_addr(HOST_IP_ADDRESS_TCP);
    sin.sin_addr.s_addr = inet_addr(ip);
    sin.sin_port = htons(port);  
    bind(listenFd, (struct sockaddr*)&sin, sizeof(sin));  
    listen(listenFd, 5);
    printf("server ip is : %s \n",ip);
}  

int main(int argc, char *argv[])
{
        struct sockaddr_in servaddr, cliaddr;
        socklen_t cliaddr_len;
        int listenfd, connfd;
        char buf[REQUEST_LEN_MAX];
        char *data;
        char str[INET_ADDRSTRLEN];
        char *secWebSocketKey;
        int i,n;
        //int connected=0;//0:not connect.1:connected.
        
        int port= DEFEULT_SERVER_PORT;

        if(argc>1)
          {
            port=atoi(argv[1]);
          }
        if(port<=0||port>0xFFFF)
          {
            printf("Port(%d) is out of range(1-%d)\n",port,0xFFFF);
            return;
          }
          // create epoll /////////////////////// 
    g_epollFd = epoll_create(MAX_EVENTS);  
    if(g_epollFd <= 0) printf("create epoll failed.%d\n", g_epollFd);  
    // create & bind listen socket, and add to epoll, set non-blocking  
    InitListenSocket(g_epollFd, HOST_PORT_TCP,HOST_IP_ADDRESS_TCP);
    // event loop  
    struct epoll_event events[MAX_EVENTS];  
    printf("server running:port[%d]\n", HOST_PORT_TCP);  
    int checkPos = 0; 
          /////////////////////////////////
         
        while (1)
          {
                  
                  // wait for events to happen  
        int fds = epoll_wait(g_epollFd, events, MAX_EVENTS, 1000);  
        if(fds < 0){
            printf("epoll_wait error, exit\n");  
            break;  
        }
       for( i = 0; i < fds; i++){ 
                  struct myevent_s *ev = (struct myevent_s*)events[i].data.ptr;  
            if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN)) // read event  
            {  
                ev->call_back(ev->fd, events[i].events, ev->arg);  
            }  
            if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) // write event  
            {  
                ev->call_back(ev->fd, events[i].events, ev->arg);  
            }  
        }                
        }//while
        close(connfd);
}

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧