搜尋

首頁  >  問答  >  主體

c++ - 用C语言socket登入斗鱼弹幕服务器的问题

根据斗鱼给的开发者手册,用struct构建出应用层协议头,发送登入请求,建立连接成功但没有从服务器传回有效消息。
代码不长,边学socket边写的注释。
抓包显示成功的建立了连接,图片放在最后。

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")
char* getIP() {
    struct hostent *host = gethostbyname("openbarrage.douyutv.com");
    if (!host) {
        printf("Get ip error\n");
        system("pause");
        exit(0);
    }
    return inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
}

struct postData{
    int data_len;//4字节
    int data_len_2;//4字节
    short message = 689;//2字节
    char secreat = 0;//1字节
    char presv = 0;//1字节
    char body[31] = "type@=loginreq/roomid@=846805/";
};
int main() {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData); //初始化
    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建socket

    //SOCKADDR为通用结构体,同时处理IPv4和IPv6
    //sockaddr_in为IPv4的结构体 sockaddr_in6为IPv6的结构体
    //SOCKADDR中IP地址和端口在一起,强制转换类型时转换
    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = PF_INET; //类型:IPv4
    sockAddr.sin_addr.s_addr = inet_addr("123.150.206.162");//getIP()返回
    /*
        sin_addr为结构体
        struct in_addr{
        in_addr_t  s_addr;  //32位的IP地址
    };
    */
    sockAddr.sin_port = htons(8601);

    connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    
    postData post_Data;
    post_Data.data_len = sizeof(post_Data);
    post_Data.data_len_2 = sizeof(post_Data);
    send(sock, (char*)&post_Data, sizeof(postData), 0);
    printf("发送成功,接收中");
    char bufRec[10000];
    recv(sock, bufRec, sizeof(bufRec), 0);
    printf("Message form server: %s\n", bufRec);

    closesocket(sock);
    //终止使用 DLL
    WSACleanup();
    system("pause");
    return 0;
}

协议组成 众所周知,受 TPC 最大传输单元(MTU)限制及连包机制影响,应用层协
议需自己设计协议头,以保证丌同消息的隔离性和消息完整性。斗鱼后台协议头 设计如下:

登录请求消息 该消息用于完成登陆授权,完整的数据部分应包含的字段如下: type@=loginreq/roomid@=301712/
字段说明 type 表示为“登陆请求”消息,固定为 loginreq roomid 所登录房间的 ID

服务端消息格式 服务端端向客户端发送消息时,头部消息类型字段为 690。
2.4.1 登录响应消息 服务端返回登陆响应消息,完整的数据部分应包含的字段如下: type@=loginres/userid@=0/roomgroup@=0/pg@=0/sessionid@=0/us
ername@=/nickname@=/is_signined@=0/signin_count@=0/live_stat@
=0/npv@=0/best_dlev@=0/cur_lev@=0/

天蓬老师天蓬老师2803 天前1237

全部回覆(4)我來回復

  • 阿神

    阿神2017-04-17 14:37:57

    你這個結構體的大小,並不是你發送資料的長度。具體原因請參考結構體對齊相關內容。

    struct postData{
        int data_len;//4字节
        int data_len_2;//4字节
        short message = 689;//2字节
        char secreat = 0;//1字节
        char presv = 0;//1字节
        char body[31] = "type@=loginreq/roomid@=846805/";
    };

    上面結構體的sizeof結果可能是4+4+2+1+1 + 32 = 44。最後的body會對齊到32位元組。

    我用下面的程式碼測試了一下,連接不上。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <errno.h>
    
    struct postData{
        int32_t data_len;        //4字节
        int32_t data_len_2;        //4字节
        int16_t message;        //2字节
        int8_t secreat;            //1字节
        int8_t presv;            //1字节
        char body[0];   // = "type@=loginreq/roomid@=846805/";
    };
    
    
    int main()
    {
        int fd;
        struct sockaddr_in addr;
    
        if((fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
            perror("socket");
            return -1;
        }
    
        memset(&addr,0,sizeof addr);
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr("123.150.206.162");
        addr.sin_port = htons(8601);
    
        if(connect(fd,(struct sockaddr*)&addr,sizeof addr) == -1){
            perror("connect"); // 连接出错,输出  connect: Connection refused
            return -2;
        }
    
        struct postData* pdata = (struct postData*)malloc(64);
        strcpy(pdata->body,"type@=loginreq/roomid@=846805/");
        pdata->data_len = pdata->data_len_2 = sizeof(*pdata) + strlen(pdata->body) +1;
        printf("pdata->data_len = %d\n",pdata->data_len);
        pdata->message = 689;
        pdata->secreat = pdata->presv = 0;
        
        if(send(fd,pdata,pdata->data_len,0) != pdata->data_len){
            puts("send 未完成");
            return -3;
        }
    
        char buf[1024];
        if(recv(fd,buf,sizeof(buf),0) == -1){
            perror("recv");
            return -4;
        }
        printf("接收到数据:%s\n",buf);
        
        close(fd);
        return 0;
    }

    回覆
    0
  • ringa_lee

    ringa_lee2017-04-17 14:37:57

    早上又試了下,因為鬥魚它說除了登錄請求還有其他的一些心跳檢測,入組請求之類的,我都發送一遍後倒是有回應

    type@=error/code@=51/.

    應該是我結構體的構造有問題吧,C語言很多小東西不紮實,不是Socket的問題~

    回覆
    0
  • 迷茫

    迷茫2017-04-17 14:37:57

    首先,body體官方不是說了有個'0'嗎?所以你發的訊息的這個body數組至少也得32吧。從效能以及內部記憶體對齊來考慮的話,不要吝惜這一點點儲存空間,用 N * PAGE_SIZE的陣列大小好一點吧。

    回覆
    0
  • PHPz

    PHPz2017-04-17 14:37:57

    請問你是在看的什麼書學的,怎麼用到鬥魚了?

    回覆
    0
  • 取消回覆