apurv625 Ответов: 1

Клиентский код резко прерывается при подключении к серверу на C++ через winsock API


Недавно я делал многопоточное серверное приложение, которое способно подключаться к нескольким клиентам. Но когда я запустил клиентское приложение, произошло некоторое исключение библиотеки времени выполнения с диалоговым окном "Debug Assertion Failed". Я не в состоянии выяснить точную причину этой ошибки. Я использую winsock API в Visual Studio Ultimate 2013. Может ли кто-нибудь помочь мне найти решение? Любая помощь будет очень признательна!

Что я уже пробовал:

Клиент:
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <signal.h>
#include <stdio.h>

using namespace std;

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

//DECLARATIONS
//error trapping signals
#define SIGINT 2
#define SIGKILL 9
#define SIGQUIT 3
// SOCKETS
SOCKET sock, client;

void s_handle(int s)
{
    if (sock)
        closesocket(sock);
    if (client)
        closesocket(client);
    WSACleanup();
    Sleep(10);
    cout << "EXIT SIGNAL :" << s;
    exit(0);
}


void s_cl(char *a, int x)
{
    cout << a;
    s_handle(x + 1000);
}


int main()
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    //SetConsoleTitle(".:: Basic Echo Client By KOrUPt 07 ::. ");


    //Declarations
    DWORD poll;
    int res, i = 1, port = 999;
    char buf[100];
    char msg[100] = "";
    char ip[15];
    WSADATA data;

    signal(SIGINT, s_handle);
    signal(SIGKILL, s_handle);
    signal(SIGQUIT, s_handle);

    cout << "\t\tEcho This is a Client";

    cout << "\n\n\n\t\tEnter IP to connect to: ";
    gets(ip);

    sockaddr_in ser;
    sockaddr addr;


    ser.sin_family = AF_INET;
    ser.sin_port = htons(123);                    //Set the port
    ser.sin_addr.s_addr = inet_addr(ip);      //Set the address we want to connect to

    memcpy(&addr, &ser, sizeof(SOCKADDR_IN));

    res = WSAStartup(MAKEWORD(1, 1), &data);      //Start Winsock
    cout << "\n\nWSAStartup"
        << "\nVersion: " << data.wVersion
        << "\nDescription: " << data.szDescription
        << "\nStatus: " << data.szSystemStatus << endl;

    if (res != 0)
        s_cl("WSAStarup failed", WSAGetLastError());

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);       //Create the socket
    if (sock == INVALID_SOCKET)
        s_cl("Invalid Socket ", WSAGetLastError());
    else if (sock == SOCKET_ERROR)
        s_cl("Socket Error)", WSAGetLastError());
    else
        cout << "Socket Established" << endl;



    res = connect(sock, &addr, sizeof(addr));               //Connect to the server
    if (res != 0)
    {
        s_cl("SERVER UNAVAILABLE", res);
    }
    else
    {
        cout << "\nConnected to Server: ";
        memcpy(&ser, &addr, sizeof(SOCKADDR));
    }

    char RecvdData[100] = "";
    int ret;

    while (true)
    {
        strcpy(buf, "");
        cout << "\nEnter message to send ->\n";
        fgets(buf, sizeof(buf), stdin);


        Sleep(5);
        res = send(sock, buf, sizeof(buf), 0);

        if (res == 0)
        {
            //0==other side terminated conn
            printf("\nSERVER terminated connection\n");
            Sleep(40);
            closesocket(client);
            client = 0;
            break;
        }
        else if (res == SOCKET_ERROR)
        {
            //-1 == send error
            printf("Socket error\n");
            Sleep(40);
            s_handle(res);
            break;
        }

        ret = recv(sock, RecvdData, sizeof(RecvdData), 0);
        if (ret > 0)
        {
            cout << endl << RecvdData;
            strcpy(RecvdData, "");
        }
    }

    closesocket(client);
    WSACleanup();
}


Сервер:
#include <windows.h>
#include  <stdlib.h>
#include <stdio.h>
#include <winsock.h>
#include <iostream>

using namespace std;

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

// our thread for recving commands
DWORD WINAPI receive_cmds(LPVOID lpParam)
{
    cout << "thread created\r\n";

    // set our socket to the socket passed in as a parameter   
    SOCKET current_client = (SOCKET)lpParam;

    // buffer to hold our recived data
    char buf[100];
    // buffer to hold our sent data
    char sendData[100];
    // for error checking 
    int res;

    // our recv loop
    while (true)
    {
        res = recv(current_client, buf, sizeof(buf), 0); // recv cmds

        Sleep(2);

        if (res == 0)
        {
            //MessageBox(0, "error", "error", MB_OK);
            closesocket(current_client);
            ExitThread(0);
        }

        if (strstr(buf, "hello"))
        { // greet this user
            cout << "\nrecived hello cmd";

            strcpy(sendData, "hello, greetings from server\n");
            Sleep(2);
            send(current_client, sendData, sizeof(sendData), 0);
        }
        else if (strstr(buf, "bye"))
        { // dissconnected this user
            cout << "\nrecived bye cmd\n";

            strcpy(sendData, "cya\n");
            Sleep(2);
            send(current_client, sendData, sizeof(sendData), 0);

            // close the socket associted with this client and end this thread
            closesocket(current_client);
            ExitThread(0);
        }
        else
        {
            strcpy(sendData, "Invalid cmd\n");
            Sleep(2);
            send(current_client, sendData, sizeof(sendData), 0);
        }

        // clear buffers
        strcpy(sendData, "");
        strcpy(buf, "");
    }
}

int main()
{
    printf("Starting up multi-threaded TCP server");

    // our masterSocket(socket that listens for connections)
    SOCKET sock;

    // for our thread
    DWORD thread;

    WSADATA wsaData;
    sockaddr_in server;

    // start winsock
    int ret = WSAStartup(0x101, &wsaData); // use highest version of winsock avalible

    if (ret != 0)
    {
        return 0;
    }

    // fill in winsock struct ... 
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(123); // listen on telnet port 23

    // create our socket
    sock = socket(AF_INET, SOCK_STREAM, 0);

    if (sock == INVALID_SOCKET)
    {
        return 0;
    }

    // bind our socket to a port(port 123) 
    if (bind(sock, (sockaddr*)&server, sizeof(server)) != 0)
    {
        return 0;
    }

    // listen for a connection  
    if (listen(sock, 5) != 0)
    {
        return 0;
    }

    // socket that we sendrecv data on
    SOCKET client;

    sockaddr_in from;
    int fromlen = sizeof(from);

    // loop forever 
    while (true)
    {
        // accept connections
        client = accept(sock, (struct sockaddr*)&from, &fromlen);
        cout << "Client connected\r\n";

        // create our recv_cmds thread and parse client socket as a parameter
        CreateThread(NULL, 0, receive_cmds, (LPVOID)client, 0, &thread);
    }

    // shutdown winsock
    closesocket(sock);
    WSACleanup();

    // exit
    return 0;
}

Patrice T

Диалоговое окно "ошибка утверждения отладки"
Дайте полное сообщение об ошибке !

apurv625

Ошибка возникла из-за объявления сигнала #define SIGKILL 9,
#определите SIGQUIT 3. Когда я удалил эти объявления, он начал работать нормально. Является ли это хорошим подходом для удаления этих деклараций?

Richard MacCutchan

Нет, это не очень хорошая идея. Сначала вам нужно отладить приложение и выяснить причину утверждения.

Jochen Arndt

Затем пусть приложение остановится и будет отлажено, чтобы узнать, где оно терпит неудачу.

Диалоговое окно утверждения содержит информацию об исходном файле и номере строки, в которой произошло утверждение. Вы можете открыть исходный файл, чтобы проверить, в какой функции произошла ошибка и какой переменный / параметр является недопустимым. Затем проверьте в своем коде, где эта функция вызывается.

1 Ответов

Рейтинг:
0

Jochen Arndt

Цитата из комментария:
Ошибка возникла из-за объявления сигнала #define SIGKILL 9,
#определите SIGQUIT 3. Когда я удалил эти объявления, он начал работать нормально. Является ли это хорошим подходом для удаления этих деклараций?

Похоже, вы повторно использовали какой-то код, написанный для Linux. Windows не знает SIGKILL и SIGQUIT сигналы.

Это поведение хорошо объяснено в документации:
Если sig не является одним из вышеперечисленных значений, вызывается недопустимый обработчик параметров, как определено в разделе Проверка параметров
Когда функция библиотеки времени выполнения C обнаруживает недопустимый параметр, она захватывает некоторую информацию об ошибке, а затем вызывает макрос, который обертывает недопустимую функцию диспетчеризации обработчика параметров, одну из _invalid_parameter, _invalid_parameter_noinfo или _invalid_parameter_noinfo_noreturn. Вызываемая функция диспетчеризации зависит от того, является ли ваш код соответственно отладочной сборкой, розничной сборкой или ошибка не считается восстановимой.

Обратите внимание также, что в вашем коде есть четыре ошибки:

  1. Обработчик использует глобальные переменные sock и client которые являются неинициализированными.
  2. Вышеупомянутые глобальные переменные никогда не изменяются (всегда неинициализируются), потому что вы используете локальные переменные с одинаковыми именами в своей системе. main() функция.
  3. Вы вызываете обработчик непосредственно из своего кода, что не рекомендуется.
  4. Обработчик вызывает функции, которые не могут быть вызваны из обработчика сигналов.
Пункты 1 и 2 на самом деле не волнуют, потому что WSACleanup() все равно закроет все открытые розетки, так что closesocket() звонки могут быть удалены.

Что касается обработчика сигналов, то ознакомьтесь с документацией:
Не выпускайте низкоуровневый или STDIO.H процедуры ввода-вывода (например, printf или fread).

Не вызывайте процедуры кучи или любую другую процедуру, использующую процедуры кучи (например, malloc, _strdup или _putenv). Дополнительные сведения см. В разделе malloc.

Не используйте функцию, которая генерирует системный вызов (например, _getcwd или time).
Ваш код нарушает все вышеперечисленное (не напрямую, но все эти типы функций вызываются за кулисами из функций, используемых в обработчике).

Вы должны создать две функции:
Функция, вызываемая из вашего кода, когда вы хотите выйти, и обработчик сигнала.

Но я не вижу причин иметь здесь обработчика сигналов. Если вы хотите справиться Ctrl+C с консольными приложениями Windows используйте Функция SetConsoleCtrlHandler - консоль Windows | Microsoft Docs[^] вместо того, чтобы signal функция.