c - 如何确保套接字 I/O 的 NULL 终止字符串 C 编程

标签 c string multithreading socket.io c-strings

我正在尝试让服务器和客户端进行通信。客户端有两个线程,一个用于写入套接字,一个用于从套接字读取。

最初,服务器输出初始问候语并为客户端提供一些选项。客户读起来很好。但是,一旦客户端写入套接字,然后尝试从套接字读取,userOut while 循环就会继续打印并开始在无限循环中混合提示。

我相信发生这种情况是因为我没有正确处理字符串,并且它们不是 NULL 终止,因此客户端只是继续读取。

有没有一种安全、直观的方法来保证我读取和写入的字符串都是 256 字节 NULL 终止的字符串? (我不在乎它是否被砍掉)

这是客户端的两个线程

/*
 *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
        bzero(bufferS, 256);

        sleep(2);
        bytesRead = read(sock, bufferS, sizeof(bufferS));       
        if (bytesRead <= 0)
            error("ERROR read failed");

        printf("%s\n", bufferS);
    }

    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
        bzero(bufferU, 256);

        sleep(2);
        scanf(" %256s", bufferU);

        bytesWritten = write(sock, bufferU, strlen(bufferU) + 1);
        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);
}

这是在服务器端管理每个客户端的线程

/*
 * 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) + 1);

    return;
}

/*
 * 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);
    //bzero(message, 256);

    // 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));
    printf("wrote %d", bytesRead);
    prompt(sock);

    while(keepRunning)
    {
        // zero out buffer
        bzero(buffer, 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
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        strcpy(optionBuffer, "open");
        if ((strncmp(buffer, optionBuffer, 4)) == 0)
        {
            printf(" USER CHOSE OPEN");
            // 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";
                        printf("account made");
                        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) + 1);
            }
        }

        // check if client chose Start
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        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));
                bzero(message, 256);
                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));
                        }
                        prompt(sock);
                    }
                }
            }
            else
            {
                // tell client no matching account exists
                message = "No matching account exists";
                write(sock, message, strlen(message));
                prompt(sock);
            }
        }

        // client has chosen exit disconnect
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        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
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        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
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        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
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        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);
            }
        }

        // client has chosen finish
        bzero(optionBuffer, MAX_ACCOUNT_NAME + 6);
        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) + 1);

                // 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);
}

最佳答案

编写以空终止符写入 256 字节字符串的代码。编写读取 256 字节以 null 结尾的字符串的代码。仅使用这两个函数在连接上进行读取和写入。确实就是这么简单。

关于c - 如何确保套接字 I/O 的 NULL 终止字符串 C 编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34167789/

相关文章:

c - 来自 Charles Petzold 的纯 C 中的 MsgBoxPrintf(win32 api) 提供中文输出...我做错了什么?

无法更改 char * c 指向的地址

c - 这个 eclipse 文件紫色资源管理器图标是什么?

c++ - 编译时字符串 : constructor overload precedence between `const char *` / `const char[]`

Python 多线程多解释器 C API

c++ - 用户在输出文本时输入控制台

c - C 语言的设计原理 DRY?

c - 动态字符串数组

string - 如何查找子字符串在给定字符串中出现的次数(包括连接)?

java - 并发-如何使其排队而不被拒绝?