search

Home  >  Q&A  >  body text

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/

天蓬老师天蓬老师2773 days ago1210

reply all(4)I'll reply

  • 阿神

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

    The size of your structure is not the length of the data you send. For specific reasons, please refer to the related content of structure alignment.

    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/";
    };

    The sizeof result of the above structure may be 4+4+2+1+1 + 32 = 44. The last body will be aligned to 32 bytes.

    I tested it with the code below and it couldn’t connect.

    #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;
    }

    reply
    0
  • ringa_lee

    ringa_lee2017-04-17 14:37:57

    I tried again in the morning, because Douyu said that in addition to login requests, there are other heartbeat detections, group membership requests, etc. I sent them all and there was a response

    type@=error/code@=51/.

    There should be something wrong with the construction of my structure. Many small things in C language are not solid. It is not a problem with Socket~

    reply
    0
  • 迷茫

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

    First of all, didn’t the body officially say there is a ‘0’? So the body array of the message you sent must be at least 32. From the perspective of performance and internal memory alignment, don't begrudge this little storage space. It would be better to use an array size of N * PAGE_SIZE.

    reply
    0
  • PHPz

    PHPz2017-04-17 14:37:57

    May I ask what book you read to learn it, and how do you use Douyu?

    reply
    0
  • Cancelreply