C ++服务器Linux机器上的分段错误 - 适用于Mac

时间:2015-09-13 22:18:13

标签: c++ linux sockets memory

我正在处理这个 HOMEWORK 任务,并且此时花了很多时间。我已经能够让我的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;
}

运行输出示例:

 ./server
 Please Enter Port Number:6666
 Server waiting...
 Segmentation fault (core dumped)

GDB的Backtrace:

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

2 个答案:

答案 0 :(得分:4)

dict1 = {1:2,3:4,5:6}
dict2 = {1:7}
dict(dict1.items() + dict2.items())

通过阅读顶部的数据,您可以在此处删除字符串。 string clientNumberString; int clientNumber = 0; n = read(newsockfd,&clientNumberString,15); 不仅仅是一个字符数组,它是一个包含指针,长度和可能的其他信息的结构。你不能只在其地址写任意数据,并期望它能够发挥作用。

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

string

在第一行之后, n = read(newsockfd,&clientNumberString,15); if (n < 0) error("ERROR reading from socket\n"); clientNumber = stoi(clientNumberString); ,只有n包含您收到的字节数。那么n如何知道要查看多少字节?另外,假设数字为&#34; 12&#34;但是stoi只读了&#34; 1&#34;因为&#34; 2&#34;还没发送过。你得错了号码。这似乎表明您从根本上不了解TCP是一个字节流。

答案 1 :(得分:0)

寻找可能出现段错误的内容,请考虑函数CheckNumber。

如果测试有可能超过50,那将是段错误。这是因为在函数结束时没有测试来确保numberClients保持在范围内。

此外,除非您已经写入了超读以进行读写,否则您的通话无法正常工作。考虑&#34;

std :: string s;

读(袜子,&amp; s,10);

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

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

char s [12];

读(袜子,s,10);

然后,如果要从那里使用字符串类,可以将该缓冲区移动到std :: string。