我正在实施一个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*) ¬Won, 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*) ¬Won, 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;
}
答案 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:mutex
和pthread_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;
}