简体   繁体   中英

c++ winsock irc client questions

I'm attempting to make a IRC chat bot using Winsock in C++. I am a newb at programming, and am new to programming with sockets.

I am attempting to connect to my Twitch channel. I can make the connection successfully, and pass several buffers (namely, my bot's password or oauth token, user name, and what channel I am trying to join).

However, when I call recv() , there's no data being sent from the Twitch server.

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define DEFAULT_BUFLEN 512

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <cstdlib>
#include <iostream>

#pragma comment(lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


using namespace std;

int main()
{
    string Buffer;
    char  buffers[1024 * 8] = { "0" };
    string oauth = "oauthtoken";
    string nick = "text_adventure_bot";
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    int iResult;
    string hostname = "irc.chat.twitch.tv";
    struct addrinfo *result = NULL,
        *ptr = NULL,
        hints;


    WSABUF DataBuf;
    WSADATA  wsadata;
    WORD wVersionRequested;

    WORD DllVersion = MAKEWORD(2, 1);
    iResult = WSAStartup(MAKEWORD(2, 2), &wsadata);
    if(iResult != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    SOCKADDR_IN addr; //the ip
    int sizeofaddr = sizeof(addr);
    addr.sin_addr.s_addr = inet_addr("52.25.27.117");
    addr.sin_port = htons(6667);
    addr.sin_family = AF_INET;

    SOCKET sock = socket(AF_INET, SOCK_STREAM, NULL);

    if (connect(sock, (SOCKADDR*)&addr, sizeofaddr) != 0)
    {
        cout << "Connection error" << endl;
    }

    cout << "connected" << endl;

    Buffer = "PASS " + oauth;

    send(sock, Buffer.c_str(), (int)strlen(Buffer.c_str()), 0); 
    recv(sock, buffers, 1024 * 8, 0);
    cout << Buffer.c_str() << endl << buffers << endl << endl;

    Buffer + "NICK " + nick;

    send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
    recv(sock, buffers, 1024 * 8, 0);
    cout << Buffer.c_str() << endl << buffers << endl << endl;

    while (true) {
        recv(sock, buffers, 1024 * 8, 0);
        cout << buffers << endl << endl;
        if (buffers[0] == 'PING') {
            Buffer = "PONG :" + hostname + "\r\n";
            send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
            cout << Buffer.c_str() << endl << buffers << endl << endl;
        }
    }
    return 0;
}

When I run this, all I see are my variable being passed, and then an infinite amount of zeros.

There are a number of problems with your code.

  1. You are not checking the return value of socket() for error (I assume you are calling WSAStartup() beforehand, right?).

  2. You are not sending any line breaks at the end of your PASS and NICK commands. IRC is a line-based protocol. That is why you are not getting any data from the server - it is waiting for you to complete your commands first.

  3. various reserved characters in IRC must be escaped.

  4. You are sending the PASS command twice, as you are using the + operator instead of the = operator when setting up your NICK command.

  5. you are not sending any USER and JOIN commands.

  6. You should not be using strlen() to calculate the length of a std::string . It has its own length() and size() methods for that purpose.

  7. You are ignoring the return values of send() and recv() . TCP is a byte stream, but you are not taking into account that send() and recv() can return fewer bytes than requested. You need to call them in a loop until you have sent/receive all of the bytes you are expecting.

Try something more like this instead:

#include <windows.h>
#include <winsock.h>

#include <iostream>
#include <string>
#include <algorithm>

void replaceStr(std::string &str, const std::string &oldStr, const std::string &newStr)
{
    std::string::size_type index = 0;
    do
    {
        index = str.find(oldStr, index);
        if (index == std::string::npos)
            return;

        str.replace(index, oldStr.length(), newStr);
        index += newStr.length();
    }
    while (true);
}

std::string quote(const std::string &s)
{
    std::string result = s;
    replaceStr(result, "\x10", "\x10""\x10");
    replaceStr(result, "\0", "\x10""0");
    replaceStr(result, "\n", "\x10""n");
    replaceStr(result, "\r", "\x10""r");
    return result;
}

std::string unquote(const std::string &s)
{
    std::string result = s;
    std::string::size_type len = result.length();
    std::string::size_type index = 0;
    while (index < len)
    {
        index = result.find("\x10", index);
        if (index = std::string::npos)
            break;

        result.erase(index, 1);
        --len;

        if (index >= len)
            break;

        switch (result[index])
        {
            case '0':
                result[index] = '\0';
                break;
            case 'n':
                result[index] := '\n';
                break;
            case 'r':
                result[index] = '\r';
                break;
        }

        ++index;
    }

    return result;
}

std::string fetch(std::string &s, const std::string &delim)
{
    std::string result;
    std::string::size_type pos = s.find(delim);
    if (pos == std::string::npos)
    {
        result = s;
        s = "";
    }
    else
    {
        result = s.substr(0, pos);
        s.erase(0, pos+delim.length());
    }
    return result;
}

bool sendStr(SOCKET sock, const std::string &s)
{
    const char *ptr = s.c_str();
    int len = s.length();

    while (len > 0)
    {
        int ret = send(sock, ptr, len, 0);
        if (ret == SOCKET_ERROR)
        {
            std::cout << "send() error: " << WSAGetLastError() << std::endl;
            return false;
        }
        ptr += ret;
        len -= ret;
    }

    return true;
}

bool sendCmd(SOCKET sock, const std::string &cmd)
{
    std::cout << "Sending: " << cmd << std::endl;
    return sendStr(sock, quote(cmd)) && sendStr(sock, "\r\n");
}

int main()
{
    int exitCode = -1;

    WSADATA wsa;
    int ret = WSAStartup(MAKEWORD(2, 0), &wsa);
    if (ret != 0)
    {
        std::cout << "Winsock init error: " << ret << std::endl;
        goto done;
    }

    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock == INVALID_SOCKET)
    {
        std::cout << "socket() error: " << WSAGetLastError() << std::endl;
        goto done;
    }

    SOCKADDR_IN addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("52.25.27.117"); //the ip
    addr.sin_port = htons(6667);

    if (connect(sock, (SOCKADDR*)&addr, sizeof(addr)) != 0)
    {
        std::cout << "connect() error: " << WSAGetLastError() << std::endl;
        goto cleanup:
    }

    std::cout << "connected" << std::endl;

    std::string oauth = ...;
    std::string nick = ...;
    std::string user = ...;
    std::string channel = ...;

    sendCmd("PASS " + oauth);
    sendCmd("NICK " + nick);
    sendCmd("USER " + user);
    sendCmd("JOIN " + channel);

    char buf[1024];
    std::string LineBuffer;
    std::string::size_type StartIdx = 0;

    do
    {
        int ret = recv(sock, buf, sizeof(buf), 0);
        if (ret == SOCKET_ERROR)
        {
            std::cout << "recv() error: " << WSAGetLastError() << std::endl;
            goto cleanup;
        }

        if (ret == 0)
        {
            std::cout << "Server disconnected" << std::endl;
            break;
        }

        LineBuffer.append(buf, ret);

        do
        {
            std::string::size_type pos = LineBuffer.find('\n', StartIdx);
            if (pos == std::string::npos)
                break;

            std::string::size_type len = pos;
            if ((pos > 0) && (LineBuffer[pos-1] == '\r'))
                --len;

            std::string msg = unquote(LineBuffer.substr(0, len));
            LineBuffer.erase(0, pos+1);
            StartIdx = 0;

            std::string senderNick;
            std::string senderHost;

            if (!msg.empty() && (msg[0] == ':'))
            {
                std::string tmp = fetch(msg, " ");
                tmp.erase(0, 1); // remove ':'
                senderNick = fetch(tmp, "!");
                senderHost = tmp;
            }

            std::cout << "Received: " << msg << std::endl;

            if (msg == "PING")
                sendCmd("PONG :" + hostname);
        }
        while(true);
    }
    while (true);

    exitCode = 0;

cleanup:
    closesocket(sock);

done:
    return exitCode;
}

First, you ignore the return value of recv , so you have no idea how many bytes you received.

Second, nowhere did you actually implement the IRC message protocol (see section 2.3 of RFC1459). So you have no reason to assume that the first byte of your buffer will contain the first byte of the IRC protocol message. Only an actual implementation of the IRC message protocol can produce a buffer whose first byte is the first byte of the IRC message.

Similarly, you can't do this:

    cout << buffers << endl << endl;

The operator<<(const char *) for a stream needs a pointer to a C-style string. Until you parse the data you received from the TCP connection and produce a C-style string from it, you must not treat it as a C-style string.

Also:

    if (buffers[0] == 'PING') {

You really meant PING as a multi-byte character constant? There is no multibyte character named PING so far as I know. And, in any event, an IRC server sends the literal four character string "PING", not a single 'PING' character.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM