客户端断开连接时TCP套接字选择服务器分段故障

时间:2014-03-21 14:46:57

标签: c++ linux sockets pthreads posix

这是一个简单的客户端服务器系统。服务器使用select来处理不同的客户端请求。但问题是:当我关闭客户端时,服务器会出现分段错误。我不知道如何处理这种情况。 谢谢你的帮助。

客户端:

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <queue>
#include <cstdlib>
#include <string.h>
#include <mutex>
#include <thread>
#include <pthread.h>
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
#include <stdlib.h>     /* for atoi() and exit() */
#include <string.h>    /* for memset() */
#include <unistd.h> /* for close() */
#include <ctype.h>
#define SIZESTACKSPACE 1000000;
#define RCVBUFSIZE 32   /* Size of receive buffer */
using namespace std;

void *sendRequest(void *);
void *receiveRequest(void *);

//#define TEMPPORTNO "40868";
//#define IP1 "10.10.154.41";
//#define IP1 "192.168.37.166";


int sock;                        /* Socket descriptor */
bool running1 = true, running2 = true;

//queue1 is used for sending message
//queue2 is used by recieve to display message
queue<char*> queue1, queue2;

//create two mutex for two queue
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

//create conditional variables
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER;

pthread_attr_t attr;
int main(void){
    pthread_t thread2, thread1;
    int rc2, rc1;

    size_t stacksize;

    //initialize attributes
    pthread_attr_init(&attr);
    pthread_attr_getstacksize (&attr, &stacksize);
    stacksize = sizeof(double)*1000+SIZESTACKSPACE;

    pthread_attr_setstacksize(&attr, stacksize);

    //create thread1, with function sendRequest()
    rc1 = pthread_create(&thread1, &attr, sendRequest, NULL);
    if(rc1){
        cout<<" ERROR; return code from pthread_create() is " << rc1;
        exit(-1);
    }
    //create thread2, with the function recieveRequest()
    rc2 = pthread_create(&thread2, &attr, receiveRequest, NULL);
    if(rc2){
        cout<<" ERROR; return code from pthread_create() is " << rc2;
        exit(-1);
    }

    int chunksize = 4; /* use BUFSIZ-1 for (probably) more efficient program */
    char *s;
    char *temp;
    int buffersize;
    int nPos;
    char c;
    char *str;

    while(1){
        buffersize = chunksize+1;

        s = (char*) malloc(buffersize); /* get the first chunk */
        if (s == NULL) {
            printf("Can't allocate %d bytes\n", buffersize);
            exit(1);
        }

        if((c=getchar()) != '\n' && c != EOF){
            nPos = 1;
            s[0] = c;
            while((c=getchar()) != '\n' && c != EOF){
                s[nPos] = c;
                if(nPos>=buffersize){
                    buffersize += chunksize;
                    temp = (char*)realloc(s, buffersize);
                    if (temp == NULL) {
                        printf("Can't realloc %d bytes\n", chunksize);
                        free(s); /* clean up the previously allocated stuff */
                        s = NULL;
                        exit(1);
                    }
                    s=temp;
                }
            nPos++;
            }

            int k, j;

            /*The last character is null*/
            //nPos is the length of the string, aside of the null character
            s[nPos] = '\0';

            //to store the new string, allocation
            str = (char*) malloc(nPos+6);

            j = nPos;
            //each byte contains a value of the number
            k = 3;
            while(k>=0){
                str[k] = nPos % 10;
                nPos = nPos/10;
                k--;
            }

            str[4] = '\0';

            k = 0;
            while(k<=j){
                str[k+5] = s[k];
                k++;
            }

            str[k+5] = '\0';

            free(s);

            //add mutex here
            pthread_mutex_lock(&mutex1);
            queue1.push(str);
            //signal sendRequest
            if(!queue1.empty())
                pthread_cond_signal(&cond1);
            pthread_mutex_unlock(&mutex1);

            //signal recvRequest
            pthread_mutex_lock(&mutex2);
            queue2.push(str);
            if(!queue2.empty())
                pthread_cond_signal(&cond2);
            pthread_mutex_unlock(&mutex2);
        }else if(c==EOF){
            break;
        }
    }
    //wait for thread 2
    while(!queue2.empty());
    //signal for threads

    close(sock);

    /* Clean up and exit */
    pthread_attr_destroy(&attr);
    pthread_exit(NULL);

    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    pthread_cond_destroy(&cond1);
    pthread_cond_destroy(&cond2);

    return 0;
}

void *sendRequest(void *){


    struct sockaddr_in echoServAddr; /* Echo server address */
    unsigned short servPort;     /* server port */
    char* servIP;                    /* Server IP address (dotted quad) */
    char* echoString;                /* String to send to echo server */
    unsigned int stringLen;
    char* tempPort;


    //fetch environment variables from the system
    servIP = getenv ("SERVER_ADDRESS");
    tempPort = getenv("SERVER_PORT");

//    servIP = IP1;
//    tempPort = TEMPPORTNO;
    if(strlen(servIP) == 0 || strlen(tempPort) == 0){
        perror("DOES NOT SET ENVIRONMENT VARIABLES FOR SERVER_ADDRESS OR SERVER_PORT\n");
        exit(-1);
    }
    servPort = atoi(tempPort);
    /* Create a reliable, stream socket using TCP */
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
        printf("socket() failed\n");
        exit(1);
    }

    /* Construct the server address structure */
    memset(&echoServAddr, 0, sizeof(echoServAddr));     /* Zero out structure */
    echoServAddr.sin_family      = AF_INET;             /* Internet address family */
    echoServAddr.sin_addr.s_addr = inet_addr(servIP);   /* Server IP address */
    echoServAddr.sin_port        = htons(servPort);     /* Server port */

    /* Establish the connection to the echo server */
    if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0){
        perror("connect() failed\n");
        exit(1);
    }
    pthread_mutex_lock(&mutex1);
    while(true){
        pthread_cond_wait(&cond1, &mutex1);

        echoString = queue1.front();
        queue1.pop();

        pthread_mutex_unlock(&mutex2);

        //send a request to the server
        //determine the length that is going to be sent
        stringLen = 5;
        while(echoString[stringLen])
            stringLen++;


        /* Send the string to the server */
        if (send(sock, echoString, stringLen, 0) != (int)stringLen){
            perror("send() sent a different number of bytes than expected\n");
            exit(1);
        }



        sleep(2);//sleep for 2 seconds

    }
    return (void *) 0;
}
void *receiveRequest(void *){
    char* temp;

    char *echoBuffer;     /* Buffer for echo string */
    unsigned int echoStringLen;      /* Length of string to echo */
    unsigned int bytesRcvd;   /* Bytes read in single recv()]*/
    char  *partTemp;
    unsigned int stringLen;
    pthread_mutex_lock(&mutex2);
    while(true){
        //get the toppest value from the queue
        pthread_cond_wait(&cond2, &mutex2);
        temp = queue2.front();
        pthread_mutex_unlock(&mutex2);

        //get the length from the queue
        //cp the first four bytes
        int k = 0;

        stringLen = 0;
        while(k<4){
            stringLen = stringLen * 10 + temp[k];
            k++;
        }

        //wait for response

        echoBuffer = (char *)malloc(5);

        if((bytesRcvd = recv(sock, echoBuffer,5, 0)) <= 0){
            printf("recv() failed or connection closed prematurely\n");
            exit(1);
        }


        //totalBytesRcvd += bytesRcvd;
        //get the length of the string recv
        k=0;
        echoStringLen = 0;
        while(k<4){
            echoStringLen = echoStringLen * 10 + echoBuffer[k];
            k++;
        }

        echoBuffer = (char *)realloc(echoBuffer, echoStringLen + 1);

        //recive the rest of the string, which is a length of echoStringLen+1
        bytesRcvd = recv(sock, echoBuffer, echoStringLen+1, 0);
        echoBuffer[echoStringLen] = '\0';

        //escape the first 5 bytes for the printing
        partTemp = temp;

        k=0;
        while(k<5){

            partTemp ++;
            k++;
        }

        printf("%s\nServer: %s \n", partTemp, echoBuffer);

        free(echoBuffer);

        //pop the toppest value from the queue
        //this is useful for closing the threads
        //it can ensure all allocations are freed
        pthread_mutex_lock(&mutex2);
        queue2.pop();
        free(temp);
        pthread_mutex_unlock(&mutex2);


    }
    return (void *) 0;
}

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>

#define PORT 0

#define MAXPENDING 5


int main(){
    struct sockaddr_in serverAddress, clientAddress; // These stores the network settings
    socklen_t serverAddressLength = sizeof(serverAddress), clientAddressLength = sizeof(clientAddress);
    int serverSocketID, clientSocketID;


    if ((serverSocketID = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        perror("socket() failed");
        exit(1);
    }

    // Specifying preference for IP address and port number lookup
    memset(&serverAddress, 0, sizeof(serverAddress)); // Initialize memory for
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddress.sin_port = htons(PORT);

    if (bind(serverSocketID, (struct sockaddr *) &serverAddress, serverAddressLength) != 0) {
        perror("bind() failed");
        close(serverSocketID);
        exit(1);
    }

    // Server starts to listen
    if (listen(serverSocketID, MAXPENDING) == -1) {
        perror("listen() failed");
        close(serverSocketID);
        exit(1);
    }

    //The following code is to obtain IP address from ifaddr info from Linux
    getsockname(serverSocketID, (struct sockaddr*) &serverAddress, &serverAddressLength);
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            if(ifa->ifa_name[0] == 'e' ){
                printf("SERVER_ADDRESS %s\nSERVER_PORT %d\n", addressBuffer, (int) ntohs(serverAddress.sin_port));
                break;
            }
        } else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            if(ifa->ifa_name[0] == 'e' ){

                printf("SERVER_ADDRESS %s\nSERVER_PORT %d\n", addressBuffer, (int) ntohs(serverAddress.sin_port));
                break;
            }
        }
    }

    if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);

    // Select attributes
    int largestFileDescriptorIndex = serverSocketID;
    // We will add clients to the master list, select will use a worker copy of our master list
    fd_set master, worker;
    //initialize the file descriptor list
    FD_ZERO(&master);
    FD_ZERO(&worker);
    FD_SET(serverSocketID, &master);
    // Add keyboard to allow control over server
    FD_SET(STDIN_FILENO, &master);

    // Specify how long to block and wait for a client to do something
    struct timeval fileDescriptorWaitTime;
    // Wait for 1 second to check if there is data coming in
    fileDescriptorWaitTime.tv_sec = 1;
    fileDescriptorWaitTime.tv_usec = 0;

    int running = 1, i;
    while(running) { // This is the big red switch that makes the server run
        worker = master; // Resets the select list
        if (select(largestFileDescriptorIndex + 1, &worker, NULL, NULL, &fileDescriptorWaitTime) == -1) {
            perror("select() failed");
            close(serverSocketID);
            exit(1);
        }
        // Loop through the state of all file descriptors
        for (i = 0; i <= largestFileDescriptorIndex; i++) {
            // Check if any file descriptor changed state
            if (FD_ISSET(i, &worker)) {
                // A new client is trying to connect
                if (i == serverSocketID) {
                    // Client connect successfully
                    if ((clientSocketID = accept(serverSocketID,
                            (struct sockaddr*) &clientAddress, &clientAddressLength)) != -1) {
                        // Register client into master list
                        FD_SET(clientSocketID, &master);
                        if (clientSocketID > largestFileDescriptorIndex) {
                            // Update length of list to loop
                            largestFileDescriptorIndex = clientSocketID;
                        }
                    }

                }
                else if (i == STDIN_FILENO) { // Check keyboard input
                    fprintf(stdout, "Server is Shutting down\n");
                    getchar();
                    running = 0;
                    continue;
                }else
                {
                    char *echoBuffer;        /* Buffer for echo string */
                    int recvMsgSize;                    /* Size of received message */
                    int j;

                    echoBuffer = (char *)malloc(5);
                    /* Receive message from client, get the first 5 bytes first to know the length of the string*/
                    if ((recvMsgSize = recv(clientSocketID, echoBuffer, 5, 0)) < 0){
                        perror("recv() failed");
                        close(clientSocketID);
                        FD_CLR(clientSocketID, &master);
                    }

                    int stringLen=0, k = 0;//the length of the string

                    /*convert the char * into an int*/
                    while(k<4){
                        stringLen = stringLen*10 + (int)echoBuffer[k];
                        k++;
                    }

                    char *str; // store the string
                    //string size + 4 bytes + '\0'+strlen(string)+'\0'
                    str = (char *)malloc(stringLen + 6);
                    //put the first 5 bytes into the echo string
                    k = 0;
                    while(k<5){
                        str[k] = echoBuffer[k];
                        k++;
                    }
                    free(echoBuffer);

                    //recieve string of a length of stringLen+1, which is char num + '\0'
                    echoBuffer = (char *)malloc(stringLen+1);
                    if ((recvMsgSize = recv(clientSocketID, echoBuffer, stringLen+1, 0)) < 0){
                        perror("recv() failed");
                        close(clientSocketID);
                        FD_CLR(clientSocketID, &master);
                    }

                    //set the last char to be null
                    echoBuffer[stringLen]='\0';
                    printf("%s\n", echoBuffer);


                    //deal with the data here
                    if(echoBuffer[0] <= 'z' && echoBuffer[0]>='a')
                        echoBuffer[0] = echoBuffer[0] + 'A'-'a';

                    //operations on data except the first one
                     for( j = 1; j<stringLen; j++)
                    {
                        if(echoBuffer[j]<='z' && echoBuffer[j]>='a' && echoBuffer[j-1] == ' ')
                            echoBuffer[j] = echoBuffer[j] + 'A'-'a';
                        else if(echoBuffer[j]<='Z' && echoBuffer[j]>='A' && echoBuffer[j-1] != ' ')
                            echoBuffer[j] = echoBuffer[j] + 'a'-'A';
                    }

                     //store the data into str
                    k= 0;
                    while(k<=stringLen){
                        str[k+5] = echoBuffer[k];
                        k++;
                    }
                    str[stringLen+5] = '\0';
                    free(echoBuffer);

                    recvMsgSize = stringLen+6;
                    /* Send received string */
                    /* Echo message back to client */
                    if (send(clientSocketID, str, recvMsgSize, 0) != recvMsgSize){
                        perror("send() failed");
                        close(clientSocketID);
                        FD_CLR(clientSocketID, &master);
                    }
                    free(str);

                }//operations on the data finishes
            }//if the client socket descriptor is in the list
        }//loop through all the file descriptors
    }//busy waiting

    close(serverSocketID);
    return 0;
}

1 个答案:

答案 0 :(得分:0)

当客户端断开连接时,客户端的套接字将选择为准备好读取,然后对该套接字上的所有后续recv()尝试将返回0,以指示EOF。请注意,这种情况是recv()返回0的唯一时间。看起来您的代码期望recv()在该场景中返回-1,因此它无法正确处理该情况。

此外,看起来你尝试在free()之后使用echoBuffer,这是未定义的行为,应该避免。 (事实上​​,为什么要使用malloc()和free()呢?只需在堆栈上声明一个足够大的echoBuffer,你就不必担心何时释放()它了。