通过socket c编程读/写

时间:2015-12-08 18:56:26

标签: c multithreading sockets client read-write

我正在尝试建立一个简单的多线程银行服务器。客户可以连接并更改帐户。

我已经设置了套接字和线程,一切似乎都运行正常。但我似乎无法在任何一端使读/写工作。

在客户端,我有两个线程,一个用于管理写入,另一个用于读取。 奇怪的是,在客户端,只要一个线程写入套接字,另一个线程就会读取刚刚写入该套接字的内容。

所以我的问题是为什么客户端读取(sock,bufferU,strlen(bufferU)给我相同的字符串写入(sock,bufferS,strlen(bufferS)刚刚发送。考虑到他们甚至不共享相同的堆栈不确定怎么了。

这是客户端

    /*
 *serverOut gets server output and prints to client.
 */
void* serverOut(void* serverName)
{
    printf("Getting server output.\n");
    char* server = (char*) serverName;
    char bufferS[256];
    int bytesRead;

    while(keepRunning)
    {
        // zero out buffer
        memset(bufferS, '0', 256);

        sleep(2);
        while(read(sock, bufferS, sizeof(bufferS)) > 0)
        {
            printf("%s\n", bufferS);
        }
        if (bytesRead <= 0)
            error("ERROR read failed");
    }

    printf("serverOut ending");
    pthread_exit(NULL);
}

/*
 *userOut gets user input and writes to server.
 *If user types 'exit', then they are disconnected.
 */
void* userOut(void* ignore)
{
    printf("Sending user input.\n");
    char bufferU[256];
    int bytesWritten;
    sleep(3);

    while(keepRunning)
    {
        // zero out buffer
        memset(bufferU, '0', 256);

        sleep(2);
        scanf(" %[^\n]}", bufferU);

        bytesWritten = write(sock, bufferU, strlen(bufferU));
        if (bytesWritten <= 0)
            error("ERROR read failed");

        if(strcmp(bufferU, "exit") == 0)
        {
            keepRunning = 0;
            printf("Disconnecting from the server.\n");
            exit(0);
        }
    }

    printf("userOut ending");
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{   
    int read_threadID, write_threadID;
    int connectionStatus;
    int sock = -1;
    int portno = -1;            //server port to connect to
    int n = -1;                 //utility variable for monitoring reading/writing from/to the socket
    char buffer[256];           //char array to store data going to and coming from server
    struct sockaddr_in dest;    //struct that holds address info for building socket
    struct hostent *host;       //struct that holds infor about a machine's address
    pthread_t thread;
    pthread_t handler;

    // Check if the user entered enough agruments
    if(argc < 3)
    {
        fprintf(stderr, "usage %s hostname port.  Specify server host.\n", argv[0]);
        exit(0);
    }

    // store important info on stack
    portno = atoi(argv[2]);             //parse text as an int
    host = gethostbyname(argv[1]);      //look up IP address that matches
    if(host == NULL)
    {
        fprintf(stderr,"ERROR finding host.\n");
        exit(0);
    }

    char* serverName = argv[1];

    // Create the socket and infor user
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0) //if it doesn't work, complain and exit
        error("ERROR opening socket.\n");   
    printf("Sock created.\n");  
    printf("Please wait while we connect you to the server.\n\n");

    // zero out sockadd_in struct
    //bzero((char *)&dest, sizeof(dest));
    memset(&dest, 0, sizeof(struct sockaddr_in));

    // Prepare the sockaddr_in struct
    dest.sin_family = AF_INET; //set a flag to indicate the type of network address
    dest.sin_port = htons(5625);  //htons change the bit format of a number to network type
    if((host = gethostbyname(serverName)) == NULL)
        printf("ERROR getting address information.\n");
    else
        bcopy((char *)host->h_addr, (char *)&dest.sin_addr.s_addr, host->h_length);

    // try to connect a first time
    connectionStatus = connect(sock, (const struct sockaddr*) &dest, sizeof(dest));

    // if first try fails try every two seconds
    while(connectionStatus != 0)
    {
        printf("Establishing connection to %s.\n", serverName);
        connectionStatus = connect(sock, (const struct sockaddr*) &dest, sizeof(dest));
        perror("");
        sleep(2);
    }
    if (connectionStatus < 0 )
        error("ERROR connecting.\n");

    // We are now connected to the server.
    printf("You have connected to the server.\n");

    // build thread status variables for pthread_exit to use later
    void* readThreadStatus;
    void* writeThreadStatus;

    // build thread handles for pthread_create
    pthread_t readThread;
    pthread_t writeThread;
    pthread_t* readThreadHandle = &readThread;
    pthread_t* writeThreadHandle = &writeThread;

    // build blank pthread attribute structs and initialize them
    pthread_attr_t readThreadAttr;
    pthread_attr_t writeThreadAttr;
    pthread_attr_init(&writeThreadAttr);
    pthread_attr_init(&readThreadAttr);

    // set the initialized attribute struct so that the pthreads created will be joinable
    pthread_attr_setdetachstate(&readThreadAttr, PTHREAD_CREATE_JOINABLE);
    pthread_attr_setdetachstate(&writeThreadAttr, PTHREAD_CREATE_JOINABLE);

    // set the initialized attribute struct so that the pthreads created will be joinable
    pthread_attr_setscope(&readThreadAttr, PTHREAD_SCOPE_SYSTEM);
    pthread_attr_setscope(&writeThreadAttr, PTHREAD_SCOPE_SYSTEM);

    // build the pthreads and check for errors
    read_threadID = pthread_create(&readThread, &readThreadAttr, serverOut, (void *)readThreadStatus);
    if (read_threadID < 0)
        error("ERROR could not create session_acceptor thread");
    write_threadID = pthread_create(&writeThread, &writeThreadAttr, userOut, (void *)writeThreadStatus);
    if (write_threadID < 0)
        error("ERROR could not create print thread");

    // join communication threads
    pthread_join(readThread, &readThreadStatus);
    pthread_join(writeThread, &writeThreadStatus);

    close(sock);

    printf("You have disconnected from the server.\n");

    return 0;
}

这是服务器(我为包含所有这些代码而道歉,不确定错误在哪里)

    int makeSocket(void* port)
{
    //uint16_t portNum = *(uint16_t*) port;
    int sock;
    struct sockaddr_in name;

    // Create the socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
        error("ERROR opening socket");

    // bzero may also work here
    memset(&name, 0, sizeof(struct sockaddr_in));


    /*Prepare the sockaddr_in struct
     */
    name.sin_family = AF_INET;
    name.sin_port = htons(PORT_NUM);                // htons change the bit format of a number to network type
    name.sin_addr.s_addr = INADDR_ANY;

    // Bind socket
    if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0)
        error("ERROR on binding");

    printf(" socket made ");
    return sock;
}

/*
 * prompt function writes bank prompt to a given socket
 *
 * reciieves a socket descriptor as an int
 *
 * does not return anything
 */
void prompt(int sock)
{
    char *message, *opt1, *opt2, *opt3, *opt4, *opt5, *opt6, *opt7;

    // initialize options
    opt1 = "OPEN accountname\n";        // opens new account unless MAX_ACCOUNTS/_NAME is exceeded or accountName already exists
    opt2 = "START accountname\n";       // starts customer session 
    opt3 = "CREDIT amount\n";           // adds amount to account balance (only valid in customer session)
    opt4 = "DEBIT amount\n";            // subtracts amount from account balance (only valid in customer session)
    opt5 = "BALANCE\n";                 // returns current account balance (only valid in customer session)
    opt6 = "FINISH\n";                  // ends customer session (only valid in customer session)
    opt7 = "EXIT\n";                    // disconnets client from server
    message = "Here are your options:\n\n";

    // display options
    write(sock, message, strlen(message));
    write(sock, opt1, strlen(opt1));
    write(sock, opt2, strlen(opt2));
    write(sock, opt3, strlen(opt3));
    write(sock, opt4, strlen(opt4));
    write(sock, opt5, strlen(opt5));
    write(sock, opt6, strlen(opt6));
    write(sock, opt7, strlen(opt7));

    return;
}

/*
 * printThread is a function that displays all bank information roughly every PRINT_RATE seconds
 *
 * printThread takes no arguements because the bank is a global variable
 *
 * there is no return value
 */
void *printThread(void* theGoods)
{
    Bank *bank = (Bank*)theGoods;
    int i;

    while(1)
    {
        // try locking bank if succesfull print all account information and then unlock
        if (pthread_mutex_trylock(&bank->bank_lock) == 0)
        {
            i = 0;

            // print all account information
            for(i; i < MAX_ACCOUNTS; i++)
            {
                printf("Account ID: %s\n", bank->accounts[i].accountName);
                printf("Current Balance: %f\n", bank->accounts[i].balance);
                if(bank->accounts[i].session_flag == 1)
                    printf("Status: In Session\n\n");
                else
                    printf("Status: Not In Session\n\n");
            }

            // unlock bank_lock and wait untill it is time to print again
            pthread_mutex_unlock(&bank->bank_lock);
            sleep(PRINT_RATE);
        }
        // if bank_lock is locked wait 2 seconds and then try again
        else
            sleep(2);
    }
}

/*
 * clientServerThread interacts with each client creating a bank client interface
 *
 * requires a socket descriptor as an arg to interact with client
 *
 * thread does not return exits when client decides
 */
void *clientServerThread(void *socket_desc)
{
    int sock = *(int*)socket_desc;
    int bytesRead;
    int exitFlag = 0;
    char *message;
    char *accountInSession;
    int accountInSessionNum = MAX_ACCOUNTS + 5;
    char acName[MAX_ACCOUNT_NAME];
    char buffer[MAX_ACCOUNT_NAME + 6];
    char optionBuffer[MAX_ACCOUNT_NAME + 6];
    bzero(buffer, MAX_ACCOUNT_NAME + 6);
    bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);

    // great the new client and prompt them with the options
    printf(" Greeting the Customer");
    message = "Greetings! Welcome to Riordan&Hess bank how may we help you?\n";
    bytesRead = write(sock, message, strlen(message) + 1);
    printf("wrote %d", bytesRead);
    prompt(sock);

    while(keepRunning)
    {
        // zero out buffer
        memset(buffer, '0', MAX_ACCOUNT_NAME + 6);

        // read client choice from socket
        bytesRead = read(sock, buffer, MAX_ACCOUNT_NAME + 5);
        if (bytesRead <= 0)
            error("ERROR read failed");

        printf("PAST READ");
        // convert user input to all lower case for comparison
        int i = 0;
        while (buffer[i])
        {
            buffer[i] = tolower(buffer[i]);
            i++;
        }

        // check if client chose opt1 OPEN
        strcpy(optionBuffer, "open");
        if ((strncmp(buffer, optionBuffer, 4)) == 0)
        {
            // check if MAX_ACCOUNTS is exceeded
            if (bank.total_accounts = MAX_ACCOUNTS)
            {
                i = 0;
                strncpy(acName, buffer+5, MAX_ACCOUNT_NAME);    

                // check if matching account name already exists
                for(i; i <= MAX_ACCOUNTS; i++)
                {   
                    int accountLen;
                    accountLen = sizeof(bank.accounts[i].accountName) - 5;

                    // if matching account name already exists ask client for new name
                    if (strncmp(bank.accounts[i].accountName, acName, MAX_ACCOUNT_NAME) == 0)       
                    {
                        message = "This account name is taken please try again\n\n";
                        write(sock, message, strlen(message));
                        prompt(sock);
                    }

                    // if there are no previous matches and an empty spot is found create account
                    else if (bank.accounts[i].exists == 0)
                    {
                        // set bank & account mutex
                        pthread_mutex_lock (&bank.bank_lock);
                        pthread_mutex_lock (&bank.accounts[i].account_lock);

                        // initialize account
                        bank.total_accounts++;
                        bank.accounts[i].exists = 1;
                        bank.accounts[i].balance = 0;
                        bank.accounts[i].session_flag = 1;

                        // create session
                        accountInSession = acName;
                        accountInSessionNum = i;
                        message = "Account created and session started\n\n";
                        write(sock, message, strlen(message));

                        // unlock bank mutex
                        pthread_mutex_unlock (&bank.bank_lock);

                        prompt(sock);
                    }
                }
            }
            // inform client that MAX_ACCOUNTS was exceeded
            else
            {
                message = "We are sorry to inform you that all of our accounts are in use. Please come back and try later";
                write(sock, message, strlen(message));
            }
        }

        // check if client chose Start
        strcpy(optionBuffer, "start");
        if ((strncmp(buffer, optionBuffer, 5)) == 0)
        {   
            // check if client is already in session
            if(accountInSessionNum <= MAX_ACCOUNTS)
            {
                message = "You are already in an account session please exit and then try again";
                write(sock, message, strlen(message));
                prompt(sock);
            }
            if (accountInSessionNum > MAX_ACCOUNTS)
            {
                // check if matching account name exists
                for(i; i <= MAX_ACCOUNTS; i++)
                {
                    int accountLen;

                    strncpy(acName, buffer+6, MAX_ACCOUNT_NAME);                            // store account name on stack

                    // if matching account exists try and begin a session
                    if (strncmp(bank.accounts[i].accountName, acName, MAX_ACCOUNT_NAME) == 0)       
                    {
                        message = "Account found ";
                        write(sock, message, strlen(message));

                        // if the account is not in session begin session
                        if(bank.accounts[i].session_flag == 0)
                        {
                            // lock account mutex and start session
                            pthread_mutex_lock (&bank.accounts[i].account_lock);
                            accountInSessionNum = i;
                            accountInSession = acName;
                            bank.accounts[i].session_flag = 1;

                            message = "Session started\n\n";
                            write(sock, message, strlen(message));
                        }
                        // if the account is in session infrom client and tell them to try again
                        else
                        {
                            message = "Account requested is already in session please try again later\n\n";
                            write(sock, message, strlen(message));
                        }
                    }
                }
            }
            else
            {
                // tell client no matching account exists
                message = "No matching account exists";
                write(sock, message, strlen(message));
                prompt(sock);
            }
        }

        // client has chosen exit disconnect
        strcpy(optionBuffer, "exit");
        if ((strncmp(buffer, optionBuffer, 4)) == 0)
        {
            // change global variable to in form client session threads to shut down
            keepRunning = 0;

            // check if client is in session->disconnect
            if(accountInSessionNum <= MAX_ACCOUNTS)
            {
                bank.accounts[accountInSessionNum].session_flag = 0;
                pthread_mutex_unlock(&bank.accounts[accountInSessionNum].account_lock);
                accountInSession = NULL;
                accountInSessionNum = MAX_ACCOUNTS + 5;
            }               
        }

        // client has chosen credit add to balance
        strcpy(optionBuffer, "credit");
        if ((strncmp(buffer, optionBuffer, 6)) == 0)
        {
            char *amount;
            float creditAmount;

            if (accountInSessionNum <= MAX_ACCOUNTS)
            {
                // copy amount to new variab;e
                strncpy(amount, buffer+6, 20);
                creditAmount = (float) atof(amount);

                // add amount to balance and inform client
                bank.accounts[accountInSessionNum].balance += creditAmount;
                message = "Credit succesful";
                write(sock, message, strlen(message));

                prompt(sock);
            }
            else
            {
                // tell client that they are not in a session
                message = "You are not currently in an account session please START";
                write(sock, message, strlen(message));
                prompt(sock);
            }
        }

        // client has chosen debit subtract from balance
        strcpy(optionBuffer, "debit");
        if ((strncmp(buffer, optionBuffer, 5)) == 0)
        {
            char *amount;
            float debitAmount;

            // check if client is in session
            if (accountInSessionNum <= MAX_ACCOUNTS)
            {
                strncpy(amount, buffer+6, 20);
                debitAmount = (float) atof(amount);

                // check if client's balance is greater than the sum requested
                if (bank.accounts[accountInSessionNum].balance > debitAmount)
                {
                    bank.accounts[accountInSessionNum].balance -= debitAmount;
                    message = "Debit succesful\n\n";
                    write(sock, message, strlen(message));
                }
                else
                {
                    message = "You do not have enough funds at this time\n\n";
                    write(sock, message, strlen(message));
                }
                prompt(sock);
            }
            else
            {
                // tell client that they are not in a session
                message = "You are not currently in an account session please START";
                write(sock, message, strlen(message));
                prompt(sock);
            }
        }

        // client has requested balance
        strcpy(optionBuffer, "balance");
        if ((strncmp(buffer, optionBuffer, 7)) == 0)
        {
            if((accountInSessionNum <= MAX_ACCOUNTS))
            {
                // tell client the balance of accountInSession
                sprintf(message, "Current Balance: %f", bank.accounts[accountInSessionNum].balance);
                write(sock, message, strlen(message));
                prompt(sock);
            }
            else
            {
                // tell client the must be in session
                message = "You are not currently in an account session please START";
                write(sock, message, strlen(message));
                prompt(sock);
            }
        }

        strcpy(optionBuffer, "finish");
        if ((strncmp(buffer, optionBuffer, 6)) == 0)
        {
            // if account in session end session
            if(accountInSessionNum <= MAX_ACCOUNTS)
            {
                // end session and inform user
                accountInSession = NULL;
                accountInSessionNum = MAX_ACCOUNTS + 5;
                message = "Session closed\n\n";
                write(sock, message, strlen(message));

                // Release mutex
                pthread_mutex_unlock (&bank.accounts[i].account_lock);
            }
            else
            {
                // tell client no matching account exists
                message = "You are not currently in an account session\n\n";
                write(sock, message, strlen(message));
                prompt(sock);
            }
        }
    }
    pthread_exit(NULL);
}

/*
 * sessionAcceptorThread creates and listens to a socket spawing clientServerThead each time a client connects
 *
 * recieves bank pointer NOT USED
 *
 * does not return
 */
void *sessionAcceptorThread(void* bankList)
{
    //Bank bank = *(Bank*)bankList;
    int socket;
    int connection;
    struct sockaddr_in peer_Addr;
    int addrLen;

    // create and bind socket and wait for incoming connections
    socket = makeSocket((void *)PORT_NUM);
    if(listen(socket, LISTEN_BACKLOG) < 0)
        error("ERROR on listening");
    addrLen = sizeof(struct sockaddr_in);
    pthread_t client_server_thread;

    while(keepRunning)
    {
        // for each connection spawn clientServerThread
        while(connection = accept(socket, (struct sockaddr *) &peer_Addr,(socklen_t*) &addrLen))
        {
            int TID;
            TID = pthread_create(&client_server_thread, NULL, clientServerThread, (void *) &connection);
            if (TID < 0)
                error("ERROR could not create serv/client thread");
            else
                printf(" WE GOT A CUSTOMER ");
        }
        if (connection < 0)
            error("ERROR accept failed");
    }

    pthread_exit(NULL);
}

int main()
{   
    int SA_threadID;
    int print_threadID;
    uint16_t portNum = PORT_NUM;

    //initialize Bank struct with default values
    Bank *bank = (Bank*) malloc(sizeof(struct _Bank));

    // build thread status variables for pthread_exit to use later
    void* threadStatus0;
    void* threadStatus1;

    // build thread handles for pthread_create
    pthread_t SAthread;
    pthread_t POthread;
    pthread_t* SAthreadHandle = &SAthread;
    pthread_t* POthreadHandle = &POthread;

    // build blank pthread attribute structs and initialize them
    pthread_attr_t SAthreadAttr;
    pthread_attr_t POthreadAttr;
    pthread_attr_init(&SAthreadAttr);
    pthread_attr_init(&POthreadAttr);

    // set the initialized attribute struct so that the pthreads created will be joinable
    pthread_attr_setdetachstate(&SAthreadAttr, PTHREAD_CREATE_JOINABLE);
    pthread_attr_setdetachstate(&POthreadAttr, PTHREAD_CREATE_JOINABLE);

    // set the initialized attribute struct so that the pthreads created will be joinable
    pthread_attr_setscope(&SAthreadAttr, PTHREAD_SCOPE_SYSTEM);
    pthread_attr_setscope(&POthreadAttr, PTHREAD_SCOPE_SYSTEM);

    // build the pthreads and check for errors
    SA_threadID = pthread_create(&SAthread, &SAthreadAttr, sessionAcceptorThread, (void *)bank);
    if (SA_threadID < 0)
        error("ERROR could not create session_acceptor thread");
    print_threadID = pthread_create(&POthread, &POthreadAttr, printThread, (void *)bank);
    if (print_threadID < 0)
        error("ERROR could not create print thread");

    pthread_join(SAthread, &threadStatus0);

    printf("Server succefully shut down");
}

1 个答案:

答案 0 :(得分:0)

  

就是这样,我只是盖过了导致我的全局变量   相信我是连接的,因为它是主要的。但是当   线程试图访问它未初始化的全局变量。现在   至于为什么它回应我不知道

你刚刚得到了印象&#34;它回应了#34;因为未初始化(即零初始化)全局write(sock, bufferU, strlen(bufferU))的{​​{1}}没有写入服务器,而是直接写入文件描述符0;在类UNIX系统上,这通常出现在您的终端上。