简体   繁体   English

通过套接字进行读/写C编程

[英]read/write over a socket c programming

I am trying to set up a simple multi-threaded bank server. 我正在尝试建立一个简单的多线程银行服务器。 Where clients can connect and make changes to accounts. 客户可以在其中连接并更改帐户的位置。

I have set up the sockets and thread and everything appears to be working fine. 我已经设置了套接字和线程,一切似乎都工作正常。 But I can not seem to get the read/write to work on either end. 但是我似乎无法使读/写在任何一端都能正常工作。

On the client side I have two threads one for managing write and one for read. 在客户端,我有两个线程,一个用于管理写入,另一个用于读取。 The strange part is that on the client side whenever one thread writes to the socket the other thread reads what was just written to that socket. 奇怪的是,在客户端,每当一个线程向套接字写入内容时,另一线程就读取刚刚写入该套接字的内容。

So my question is why is the client read(sock, bufferU, strlen(bufferU) give me the same string that write(sock, bufferS, strlen(bufferS) just sent. considering they dont even share the same stack not sure whats going on. 所以我的问题是为什么客户端read(sock,bufferU,strlen(bufferU)给我的字符串与write(sock,bufferS,strlen(bufferS)刚刚发送的)相同,考虑到它们甚至不共享同一堆栈,所以不确定是怎么回事。

here is the client 这是客户

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

here is the server (I apologize for including all this code just not sure where the error would be) 这是服务器(我很抱歉包含所有这些代码,只是不确定错误会在哪里)

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

that was it I simply overshadowed my global variable leading me to believe that i was connected because it was in the main. 就是这样,我只是掩盖了我的全局变量,使我相信我是有联系的,因为它是主要的。 but when the thread tried to access the global variable it was not initialized. 但是,当线程尝试访问全局变量时,它尚未初始化。 now as to why it was echoing i have no idea 现在,为什么它回声我不知道

You just got the impression "it was echoing" because the write(sock, bufferU, strlen(bufferU)) to the uninitialized (ie zero-initialized) global sock didn't write to the server, but rather directly to file descriptor 0; 您刚刚得到的印象是“它正在回显”,因为对未初始化(即零初始化)的全局sockwrite(sock, bufferU, strlen(bufferU))并未写入服务器,而是直接写入文件描述符0。 on UNIX-like systems this typically appears on your terminal. 在类似UNIX的系统上,这通常显示在您的终端上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM