send:尝试发送数据时对非套接字和segfault错误的套接字操作

时间:2019-06-11 05:34:22

标签: c++ sockets networking

我正在实施一个hang子手游戏,并试图将一些数据发送到服务器。 当我通过VS Code和Terminal运行该代码时,该代码可以在我的Mac机器上编译并正常工作,但是在他们用于评分的学校网络上却无法运行。

当我在其中运行它时,输入玩家的名字后,它就会出现segfaluts。我试图修复它,但它要么是段错误,要么给我一个“发送:非套接字上的套接字操作”错误。

这是完整的代码:

客户代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <arpa/inet.h>
#include <cstring>
#include <algorithm>

void printDashed(std::string dashed);
int main(int argc, char *argv[]) {

    // check for correct number of argumants
    if (argc != 3) {
        std::cout << "Invalid argument..." << std::endl;
        return -1;
    }

    // store arguments 
    char *ipAddress = argv[1];
    char *port = argv[2];

    // check if port number argument valid
    for (int i = 0; i < strlen(port); i++) {
        if (isdigit(port[i] == 0)) {
            perror("invalid port number");
            return -1;
        }
    }

    // convert from string to int 
    int portNumber = atoi(port);

    int clientSocket;
    struct sockaddr_in address;

    // convert address
    if ((inet_pton(AF_INET, ipAddress, &address.sin_addr)) != 1){
        perror("inet_pton");
        return -1;
    }

    int conn;

    //////////////////////// CREATE CLIENT SOCKET //////////////////////////////
    clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == -1) {
        perror("Cannot open the socket...");
        return -1;
    }
    address.sin_family = AF_INET;
    address.sin_port = htons(portNumber);

    /////////////////////// CONNECT ////////////////////////////////////////////
    conn = connect(clientSocket, (struct sockaddr* ) &address, sizeof(address));
    if (conn < 0) {
        perror("cannot connect");
        return -1;
    }

    ///////////////////////////// start game ///////////////////////////////////
    std::string player;
    std::cout << "\nWelcome to hangman game! \n";
    std::cout << "Please, Enter your name: ";
    std::cin >> player;
    std::cout << std::endl;

    // recieve the number of letters in the word
    int wordLength;
    if (recv(clientSocket, (void*) &wordLength, sizeof(wordLength), 0) < 0){
        perror("recv");
        return -1;
    }
    //htonl(wordLength);

    // send the name to the server
    if (send(clientSocket, (void *) &player, sizeof(player), 0) < 0){
        perror("send");
        return -1;
    }

    char guess;
    bool correct;
    int numGuess;
    int numCorrect = 0;
    bool notWon = true;
    std::string dashed;
    char *buffer = new char[1000];


    int turn = 0;
    while (notWon) {

        turn++;
        if (recv(clientSocket, (void*) &dashed, sizeof(dashed), 0) < 0){
            perror("recv");
            return -1;
        }
        std::cout << "Turn " << turn << std::endl;
        std::cout << "Word: ";
        printDashed(dashed);
        std::cout << std::endl;

        // ask the user for a guess
        std::cout << "Guess a letter: " << std::endl;
        std::cin >> guess;

        // check the input for validity
        while (isdigit(guess) || islower(guess)) {
            std::cout << "You entered an invalid character!\nPlease enter a valid guess: ";
            std::cin >> guess;
        }
        // send the guess to the server
        if (send(clientSocket, (void*) &guess, sizeof(guess), 0) < 0) {
            perror("send");
            return -1;
        }

        // recive the guess response fromt he server
        if (recv(clientSocket, (void*) &correct, sizeof(correct),0) < 0) {
            perror("recv");
            return -1;
        }
        if (recv(clientSocket, (void*) &dashed, sizeof(dashed),0) < 0){
            perror("recv");
            return -1;
        }
        if (recv(clientSocket, (void*) &numGuess, sizeof(numGuess),0) < 0){
            perror("recv");
            return -1;
        }
        if (recv(clientSocket, (void*) &numCorrect, sizeof(numCorrect),0) < 0){
            perror("recv");
            return -1;
        }

        if (!correct){
            std::cout << "Incorrect guess!" << std::endl;
        } else {
            std::cout << "Correct guess!" << std::endl;
        }
        std::cout << std::endl;

        if (numCorrect == wordLength){
            std::cout << "Congratulations! You guessed the word ";
            printDashed(dashed);
            std::cout << "!\n";
            std::cout << "It took " << turn << " turns to guess the word correctly." << std::endl;
            std::cout << std::endl;

            notWon = false;
        }
        if (send(clientSocket, (void*) &notWon, sizeof(notWon), 0) < 0){
            perror("send");
            return -1;
        }
    }

    std::cout << "LEADER BOARD" << std::endl;
    recv(clientSocket, buffer, 1000, 0);
    std::cout << buffer << std::endl;
    std::cout << std::endl;
}

void printDashed(std::string dashed) {
    for(int i = 0; i < dashed.length(); i++) {
        std::cout << dashed[i];
    }
}

服务器代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <math.h>
#include <cstring>
#include <algorithm>

const std::string PATHNAME = "words.txt";

struct threadArgs {
    std::string w;
    int clientSock;
};

struct leaderBoard{
    float score;
    std::string playerName;
};

std::vector<leaderBoard> lb;
std::vector<leaderBoard>::iterator it;
pthread_mutex_t mutex1;
void* handleClientFunction(void* threadArg);
bool leaderBoardSorter(leaderBoard const& lhs, leaderBoard const& rhs);
std::string createLeaderBoardString(std::vector<leaderBoard> lb);

int main(int argc, char *argv[]){

    // check for right ammount of arguments
    if (argc != 2){
        std::cout << "Invalid argument..." << std::endl;
        return -1;
    }

    const int NUM_OF_WORDS = 57489;
    std::string words[NUM_OF_WORDS];

    std::ifstream inFile;
    inFile.open(PATHNAME);
    std::string line;
    int i = 0;
    while(inFile >> line) {
        words[i] = line;
        i++;
    }

    // store the passed argument 
    char *port = argv[1];

    // check if the argument contains all digits
    for (unsigned i = 0; i < strlen(port); i++) {
        if (isdigit(port[i]) == 0) {
            perror("invalid port number");
            return -1;
        }
    }

    // convert the passed argument to ints
    int portNumber = atoi(port);

    int listeningSocket;
    int clientSocket;
    socklen_t addressLength;
    struct sockaddr_in serverAddress;
    struct sockaddr_in clientAddress;
    int opt = 1;

    //////////////////////////////// Socket creation //////////////////////////////////////////////////////////////////
    listeningSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listeningSocket == -1) {
        perror("Cannot open the socket...");
        return -1;
    }

    if (setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
        perror("setsockport");
        return -1;
    }

    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddress.sin_port = htons(portNumber);

    ////////////////////////////// Socket binding /////////////////////////////////////////////////////////////////////
    if (bind(listeningSocket, (struct sockaddr*) &serverAddress, sizeof(serverAddress)) == -1){
        std::cout << "Binding failed" << std::endl;
        exit(EXIT_FAILURE);

    }
    ////////////////////////////////// listening //////////////////////////////////////////////////////////////////////
    if (listen(listeningSocket, 5) < 0) {
        std::cout << "Failed to listen..." << std::endl;
        exit(EXIT_FAILURE);
    }

    while (true) {
        ///////////////////////////////// accepting /////////////////////////////////////////////////////////////////////
        clientSocket = accept(listeningSocket, (struct sockaddr *) &clientAddress, &addressLength);        
        addressLength = sizeof(clientAddress);
        std::string word;

        // chose a word at random for the client to process
        srand(time(NULL));
        word = words[rand() % NUM_OF_WORDS + 1];

        if (clientSocket < 0) {
            std::cout << "Cannot accept connection" << std::endl;
            perror("accept");

        } else {
            pthread_t threadID;

            threadArgs arguments;
            arguments.w = word;
            arguments.clientSock = clientSocket;

            if (pthread_create(&threadID, NULL, handleClientFunction, (void*) &arguments) != 0){
                std::cout << "Failed to create thread..." << std::endl;
                close(clientSocket);
            }

            if (pthread_detach(threadID) != 0) {
                std::cout << "Failed to detach thread..." << std::endl;
            }
        }
    }

    return 0;
}

void* handleClientFunction(void* threadArg) {

    struct threadArgs arg = *((struct threadArgs*) threadArg);

    std::string word = arg.w;
    int clientSocket = arg.clientSock;

    bool correct;
    int numGuess = 0;

    // print the word to the screen
    std::cout << "Word to guess is: " << word << std::endl;

    int wordLength = word.length();
    //htonl(wordLength);
    // dashed representation of the word
    std::string dashed = word;
    for (int i = 0; i < wordLength; i++) {
        dashed[i] = '_';
    }

    // send the numeber of letters to the client
    if (send(clientSocket, (void*) &wordLength, sizeof(wordLength), 0) < 0){
        perror("send");
        return (void*) -1;
    }

    std::string name;
    // recieve client player name from client
    if (recv(clientSocket, (void*) &name, sizeof(name), 0) < 0){
        perror("recv");
        return (void*) -1;
    }

    leaderBoard playerLB;
    playerLB.playerName = name;

    char guess;
    float score;
    int countCorrect = 0;
    bool notWon = true;
    std::string lBoardString;

    while(true){

        // send dashed word for client to display
        if (send(clientSocket, (void*) &dashed, sizeof(dashed), 0) < 0){
            perror("send");
            return (void*) -1;
        }
        // recieve a guess from the client and check guess is valid
        if (recv(clientSocket, (void*) &guess, sizeof(guess), 0) < 0){
            perror("recv");
            return (void*) -1;
        }

        while (isdigit(guess) || islower(guess)) {
             std::string errorInput = "Something went wrong...\nPlease guess again.";
             if (send(clientSocket, (void*) &errorInput, sizeof(errorInput), 0) < 0){
                perror("send");
                return (void*) -1;
            }
            if (recv(clientSocket, (void*) &guess, sizeof(guess), 0) < 0){
                perror("recv");
                return (void*) -1;
            }
        }
        correct = false;
        numGuess++;
        // check if the word contains the guessed letter
        for (unsigned i = 0; i < wordLength; i++) {
            if (word[i] == guess && dashed[i] != guess) {
                correct = true;
                dashed[i] = guess;
                countCorrect++;
            }
        }
        // send the guess resonse from the server
        if (send(clientSocket, (void*) &correct, sizeof(correct), 0) < 0){
            perror("send");
            return (void*) -1;
        }        
        if (send(clientSocket, (void*) &dashed, sizeof(dashed), 0) < 0){
            perror("send");
            return (void*) -1;
        }
        if (send(clientSocket, (void*) &numGuess, sizeof(numGuess), 0) < 0){
            perror("send");
            return (void*) -1;
        }
        if (send(clientSocket, (void*) &countCorrect, sizeof(countCorrect),0) < 0){
            perror("send");
            return (void*) -1;
        }
        if (recv(clientSocket, (void*) &notWon, sizeof(notWon),0) < 0) {
            perror("recv");
            return (void*) -1;
        }

        if (!notWon){

            score = roundf(((float)numGuess / (float)wordLength) * 100) / 100;
            playerLB.score = score;
            pthread_mutex_lock(&mutex1);
            lb.push_back(playerLB);
            std::sort(lb.begin(), lb.end(), &leaderBoardSorter);
            for (it = lb.begin(); it != lb.end(); it++) {
                std::cout << *&it->playerName << std::endl;
                std::cout << *&it->score << std::endl;
            }
            lBoardString = createLeaderBoardString(lb);
            pthread_mutex_lock(&mutex1);
            send(clientSocket, (void*) lBoardString.c_str(), lBoardString.size(), 0);

            std::cout << std::endl;
            break;
        }
    }

    return (void*) 0;
}

bool leaderBoardSorter(leaderBoard const& lhs, leaderBoard const& rhs) {
    if (lhs.score != rhs.score){
        return lhs.score < rhs.score;
    }
    return lhs.score < rhs.score;
}

std::string createLeaderBoardString(std::vector<leaderBoard> lb){
    std::string leaderboard = " ";
    for (it = lb.begin(); it != lb.end(); it++) {
        leaderboard.append("\n");
        leaderboard.append("Name: ");
        leaderboard.append(*&it->playerName);
        leaderboard.append("\n");
        leaderboard.append("Score: ");
        leaderboard.append(std::to_string(*&it->score));
    }
    return leaderboard;
}

1 个答案:

答案 0 :(得分:1)

我在您的代码中看到了许多问题

最大的问题之一是您没有正确读取/发送std::string对象。您不能使用类似的东西

send(..., &string, sizeof(string), ...)

recv(..., &string, sizeof(string), ...)

std::string个对象上。

对于send(),您需要

send(..., string.c_str(), string.length(), ...)

对于recv(),它涉及更多。您需要更多类似的东西:

size = ...;
string.resize(size);
recv(..., &string[0], size, ...);

,这意味着在读取数据之前知道正在发送size。因此,您需要先发送字符串的实际length,然后再发送其数据。

您需要定义一个 protocol (即一组规则),该规则准确描述了客户端和服务器之间应该如何通信,如何格式化来回数据等等。 / p>

我在您的代码中看到的其他问题包括:

  • 没有将您的客户信息正确传递到pthread_create()

  • 在赢得比赛后处理排行榜时,无法正确解锁互斥锁。

  • 您显然正在使用C ++ 11或更高版本(由于使用std::to_string()),但是您没有使用任何C ++ 11功能,例如std::thread和{{ 1}},而不是std:mutexpthread_create,而是使用RAII进行清理,p_thread_mutex,基于范围的auto循环,for等。

话虽如此,请尝试以下类似操作:

MY_COMMON_CODE.H:

<random>

MY_COMMON_CODE.CPP:

#ifndef MY_COMMON_CODE_H
#define MY_COMMON_CODE_H

#include <string>
#include <cstdint>
#include <memory>

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>

void throwRuntimeError(const char *msg, int err);
void throwRuntimeError(const char *msg);

void sendRaw(int sock, const void *data, size_t size);
void readRaw(int sock, void *data, size_t size);

void sendChar(int sock, char value);
char readChar(int sock);

void sendBool(int sock, bool value);
bool readBool(int sock);

void sendInt32(int sock, int32_t value);
int32_t readInt32(int sock);

void sendString(int sock, const std::string &value);
std::string readString(int sock);

struct socket_deleter {
    typedef int pointer;
    void operator()(int sock) const { if (sock != -1) close(sock); }
};
using socket_ptr = std::unique_ptr<int, socket_deleter>;

#endif

客户代码:

#include <sstream>
#include <stdexcept>

#include "my_common_code.h"

void throwRuntimeError(const char *msg, int err)
{
    std::ostringstream oss;
    if (msg && *msg) oss << msg << ": ";
    oss << strerror(err);
    throw std::runtime_error(oss.str());
}

void throwRuntimeError(const char *msg)
{
    std::ostringstream oss;
    if (msg && *msg) oss << msg;
    throw std::runtime_error(oss.str());
}

void sendRaw(int sock, const void *data, size_t size)
{
    const char *pdata = static_cast<const char *>(data);
    while (size > 0) {
        ssize_t sent = send(sock, pdata, size, 0);
        if (sent < 0)
            throwRuntimeError("send", errno);
        pdata += sent;
        size -= sent;
    }
}

void readRaw(int sock, void *data, size_t size)
{
    char *pdata = static_cast<char *>(data);
    while (size > 0) {
        ssize_t recvd = recv(sock, pdata, size, 0);
        if (recvd < 0)
            throwRuntimeError("recv", errno);
        if (recvd == 0)
            throwRuntimeError("disconnected");
        pdata += sent;
        size -= sent;
    }
    return true;
}

void sendChar(int sock, char value)
{
    sendRaw(sock, &value, sizeof(value));
}

char readChar(int sock)
{
    char value;
    readRaw(sock, &value, sizeof(value));
    return value;
}

void sendBool(int sock, bool value)
{
    sendRaw(sock, &value, sizeof(value));
}

bool readBool(int sock)
{
    bool value;
    readRaw(sock, &value, sizeof(value));
    return value;
}

void sendInt32(int sock, int32_t value)
{
    value = htonl(value);
    sendRaw(sock, &value, sizeof(value));
}

int32_t readInt32(int sock)
{
    int32_t value;
    readRaw(sock, &value, sizeof(value));
    return ntohl(value);
}

void sendString(int sock, const std::string &value)
{
    int32_t size = value.length();
    sendInt32(sock, size);
    sendRaw(sock, s.c_str(), size);
}

std::string readString(int sock)
{
    std::string value;
    int32_t size = readInt32(sock);
    if (size > 0) {
        value.resize(size);
        readRaw(sock, &value[0], size);
    }
    return value;
}

服务器代码:

#include <iostream>
#include <limits>
#include <cstring>
#include <cstdlib>

#include "my_common_code.h"

int main(int argc, char *argv[]) {

    try {
        // check for correct number of arguments
        if (argc != 3)
            throwRuntimeError("Invalid argument count");

        // store arguments 
        char *ipAddress = argv[1];
        char *port = argv[2];

        // check if port number argument valid
        int portNumber = std::atoi(port);
        if ((portNumber <= 0) || (portNumber > 65535))
            throwRuntimeError("Invalid port number");

        sockaddr_in address = {};
        address.sin_family = AF_INET;
        address.sin_port = htons(static_cast<uint16_t>(portNumber));

        // convert address
        if (inet_pton(AF_INET, ipAddress, &address.sin_addr) != 1)
            throwRuntimeError("Invalid IP address");

        //////////////////////// CREATE CLIENT SOCKET //////////////////////////////
        socket_ptr clientSocketPtr( socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) );
        int clientSocket = clientSocketPtr.get();
        if (clientSocket == -1)
            throwRuntimeError("socket", errno);

        /////////////////////// CONNECT ////////////////////////////////////////////
        if (connect(clientSocket, reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0)
            throwRuntimeError("connect", errno);

        ///////////////////////////// start game ///////////////////////////////////
        std::cout << "\nWelcome to hangman game! \n";
        std::cout << "Please, Enter your name: ";

        std::string player;
        std::getline(std::cin, player);
        std::cout << std::endl;

        // send the name to the server
        sendString(clientSocket, player);

        char guess;
        bool correct;
        int numGuess = 0;
        int32_t numCorrect = 0;
        std::string dashed;

        do {
            ++numGuess;

            dashed = readString(clientSocket);

            std::cout << "Turn: " << numGuess << std::endl;
            std::cout << "Word: " << dashed << std::endl;
            std::cout << std::endl;

            // ask the user for a guess
            std::cout << "Guess a letter: " << std::endl;
            if (!(std::cin >> guess))
                throwRuntimeError("Input error");

            // send the guess to the server
            sendChar(clientSocket, guess);

            // receive the guess response from the server

            correct = readBool(clientSocket);
            numCorrect = readInt32(clientSocket);

            if (correct)
                std::cout << "Correct guess!" << std::endl;
            else
                std::cout << "Incorrect guess!" << std::endl;
            std::cout << std::endl;
        }
        while (numCorrect < dashed.length());

        std::cout << "Congratulations! You guessed the word " << dashed << "!\n";
        std::cout << "It took " << numGuess << " turn(s) to guess the word correctly." << std::endl;
        std::cout << std::endl;

        std::string leaderBoard = readString(clientSocket);

        std::cout << "LEADER BOARD" << std::endl;
        std::cout << leaderBoard << std::endl;
    }
    catch (const std::exception &ex) {
        std::cerr << ex.what() << std::endl;
        return -1;
    }

    return 0;
}