C++服务器上的分段错误——MAC的工作

C++服务器上的分段错误——MAC的工作,c++,linux,sockets,memory,C++,Linux,Sockets,Memory,我正在做这项家庭作业,在这一点上花了相当多的时间。我已经能够让我的Mac电脑完美地运行客户端和服务器,但要求它必须在Linux机器上运行,这就是我发现它的主要问题所在。在客户端连接到服务器之后,我立即收到一个分段错误,它发生在两条cout线路之间,这对我来说毫无意义。我花了大约4个小时来研究这一点,但运气不好。有人能给我一些有用的建议吗?提前谢谢 Server.cpp //header files //input - output declarations included in all C

我正在做这项家庭作业,在这一点上花了相当多的时间。我已经能够让我的Mac电脑完美地运行客户端和服务器,但要求它必须在Linux机器上运行,这就是我发现它的主要问题所在。在客户端连接到服务器之后,我立即收到一个分段错误,它发生在两条cout线路之间,这对我来说毫无意义。我花了大约4个小时来研究这一点,但运气不好。有人能给我一些有用的建议吗?提前谢谢

Server.cpp

//header files

//input - output declarations included in all C programs
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <stdexcept>
#include <iostream>

//contains definitions of a number of data types used in system calls
#include <sys/types.h>

//definitions of structures needed for sockets
#include <sys/socket.h>

//in.h contains constants and structures needed for internet domain addresses
#include <netinet/in.h>

using namespace std;

int* clientNumbers  = new int[50];

void initializeArray()
{
    for(int i = 0; i < 50; i++)
    {
        clientNumbers[i] = 0;
    }
}

//This function is called when a system call fails. It displays a message about the error on stderr and then aborts the program.
void error(const char *msg)
{
    perror(msg);
    exit(1);
}
int numberClients = 0;
bool checkNumber(int number)
{
    for(int i = 0; i < 50; i++)
    {
        if(number == clientNumbers[i])
        {
            return false;
        }
    }
    clientNumbers[numberClients] = number;
    numberClients++;
    return true;
}


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

    pid_t childPID;

    initializeArray();

    //sockfd and newsockfd are file descriptors,These two variables store the values returned by the socket system call and the accept system call.
    //portno stores the port number on which the server accepts connections.
    int sockfd, newsockfd, portno;

    //clilen stores the size of the address of the client. This is required for the accept system call.
    socklen_t clilen;

    //serv_addr will contain the address of the server, and cli_addr will contain the address of the client which connects to the server.
    struct sockaddr_in serv_addr, cli_addr;
    int n;

    //create socket
    //it take three arguments - address domain, type of socket, protocol (zero allows the OS to choose thye appropriate protocols based on type of socket)
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR opening socket");

    string portNumber;
    cout << "Please Enter Port Number:";
    getline(cin, portNumber);


    //stoi() function can be used to convert port number from a string of digits to an integer, if your input is in the form of a string.
    try{
        portno = stoi(portNumber);
    }catch(const std::invalid_argument e)
    {
        error("ERROR No Port Number Entered.");
    }


    if(portno > 65535 || portno < 1)
    {
        error("Invalid Port Number. Exiting.");
    }

    //contains a code for the address family
    serv_addr.sin_family = AF_INET;

    //contains the IP address of the host
    serv_addr.sin_addr.s_addr = INADDR_ANY;

    //contain the port number
    serv_addr.sin_port = htons(portno);

    //bind() system call binds a socket to an address, in this case the address of the current host and port number on which the server will run.
    //three arguments, the socket file descriptor, the address to which is bound, and the size of the address to which it is bound.
    if (::bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");

    //listen system call allows the process to listen on the socket for connections.
    //The first argument is the socket file descriptor, and second is number of connections that can be waiting while the process is handling a particular connection.
    listen(sockfd,5);

    while(1)
    {
        cout << "Server waiting...\n";

        //This is where the program experiences a Segmentation Fault

        cout << "Just Before Clilen";
        clilen = (socklen_t) sizeof(cli_addr);

        //accept() system call causes the process to block until a client connects to the server.
        newsockfd = accept(sockfd,
                            (struct sockaddr *) &cli_addr,
                            &clilen);

        cout << "WE MADE IT THIS FAR!";

        if (newsockfd < 0)
            error("ERROR on accept");

        string clientNumberString;
        int clientNumber = 0;
        n = read(newsockfd,&clientNumberString,15);
        if (n < 0) error("ERROR reading from socket\n");
        clientNumber = stoi(clientNumberString);

        if(checkNumber(clientNumber)) {

            //reads from the socket into a buffer for a maximum of 255 characters
            //read call uses new file descriptor, the one returned by accept()

            if(fork() == 0)
            {
                cout << "Established Connection With: " << clientNumber << "\n";
                while(1)
                {
                    string lowerCase;
                    //Read in String from Client
                    n = read(newsockfd, &lowerCase, 255);
                    if(n == 0)break;

                    //transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), ::toupper);

                    string upperCase = "";

                    for(int i = 0; i < lowerCase.length(); i++)
                    {
                        upperCase += toupper(lowerCase.at(i));
                    }

                    n = write(newsockfd, &upperCase, upperCase.length() + 1);
                }
                cout << "Disconnecting from Client\n";
                //close connections using file descriptors
                close(newsockfd);
            }

        }else
        {
            n = write(newsockfd, 0, 1);
            cout << "CONNECTION REFUSED: Client Number already Taken.\n";
            break;
        }

    }

        //
        close(sockfd);


    return 0;
}
来自GDB的回溯跟踪:

Program received signal SIGSEGV, Segmentation fault.
__GI_____strtol_l_internal (
    nptr=0x2388058 <error: Cannot access memory at address 0x2388058>, 
    endptr=0x7fffffffde00, base=10, group=<optimized out>, 
    loc=0x7ffff78ba060 <_nl_global_locale>) at ../stdlib/strtol_l.c:298
298 ../stdlib/strtol_l.c: No such file or directory.
(gdb) backtrace

#0  __GI_____strtol_l_internal (
    nptr=0x2388058 <error: Cannot access memory at address 0x2388058>, 
    endptr=0x7fffffffde00, base=10, group=<optimized out>, 
    loc=0x7ffff78ba060 <_nl_global_locale>) at ../stdlib/strtol_l.c:298
#1  0x0000000000401ca1 in __gnu_cxx::__stoa<long, int, char, int> (
    __convf=0x401350 <strtol@plt>, __name=0x401da9 "stoi", 
    __str=0x2388058 <error: Cannot access memory at address 0x2388058>, 
    __idx=0x0) at /usr/include/c++/4.8/ext/string_conversions.h:62
#2  0x0000000000401bab in std::stoi (__str=..., __idx=0x0, __base=10)
    at /usr/include/c++/4.8/bits/basic_string.h:2825
#3  0x00000000004017fd in main (argc=1, argv=0x7fffffffdfe8) at main.cpp:143
通过读取字符串顶部的数据,您可以在此处丢弃字符串。字符串不仅仅是一个字符数组,它是一个包含指针、长度和其他信息的结构。您不能只在其地址写入任意数据,然后期望它工作

此外,您似乎对TCP的工作原理有一些基本的误解:

    n = read(newsockfd,&clientNumberString,15);
    if (n < 0) error("ERROR reading from socket\n");
    clientNumber = stoi(clientNumberString);

在第一行n之后,只有n保存您接收的字节数。那么stoi应该如何知道要查看多少字节呢?另外,假设数字是12,但只读读取1,因为2尚未发送。你会打错电话的。这似乎表明,你根本不理解TCP是一个字节流。

< p>寻找可以分割的东西,考虑函数校验号。

如果测试有可能超过计数50,它将出现故障。这是因为在函数结束时,没有测试来确保numberClients保持在范围内

此外,除非您编写了读写重载,否则您的调用将无法工作。考虑

std::字符串s

阅读sock和s,10

这会在更好的编译器中发出警告,但不会起作用。以这种方式作为void*传入时,s不会作为字符串类进行操作

您必须提供一个字符数组,可能是:

chars[12]

阅读sock,s,10


然后,如果您想从此处开始使用string类,可以将该缓冲区移动到std::string中。

gdb的回溯在哪里?因为没有刷新输出,所以您实际上不知道它在哪里崩溃。就在克利伦之前;当seg故障发生时,很可能位于输出缓冲区中,等待写入控制台。我们走了这么远!也可能在缓冲区中。无法判断是否通过屏幕转储进行调试。我添加了gdb回溯。谢谢你在末尾的提示,我会做这些调整。我是C++新手!全局变量、手动动态内存和幻数都是灾难的根源。使用int clientNumbers[50]={};创建一个50秒的数组。或者更好的方法是使用一个类来表示您使用过的客户机,以及一个可以根据需要增长的向量和std::find来执行此搜索我猜您正在破坏字符串的内部结构:n=readnewsockfd,&clientNumberString,15;在尝试读取缓冲区之前,需要调整字符串的大小。可能不会收到错误消息,因为它是空的*。只要你给一个空白,一个指针,它就是幸福的。回避他们的原因之一。
    string clientNumberString;
    int clientNumber = 0;
    n = read(newsockfd,&clientNumberString,15);
    n = read(newsockfd,&clientNumberString,15);
    if (n < 0) error("ERROR reading from socket\n");
    clientNumber = stoi(clientNumberString);