2015-12-08 2 views
0

Я пытаюсь настроить простой многопоточный сервер банка. Где клиенты могут подключаться и вносить изменения в учетные записи.чтение/запись по сокету c программирование

Я установил разъемы и нитки, и все работает нормально. Но я не могу заставить чтение/запись работать с обоих концов.

На стороне клиента у меня есть два потока, один для управления записью и один для чтения. Странная часть заключается в том, что на стороне клиента всякий раз, когда один поток записывает в сокет, другой поток читает то, что было написано только в этом сокете.

Так что мой вопрос в том, почему клиент читает (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

В обход я вижу множество ошибок API-интерфейса, слишком много, чтобы их можно было найти в ответе. Совет: вырвите весь код потока и получите часть сокета для работы самостоятельно. Это будет намного проще, чем пытаться отлаживать сокеты и потоки одновременно. Также: приобретете из своей дружественной местной публичной библиотеки копии [W. Книги Ричарда Стивенса] (http://www.amazon.com/W.-Richard-Stevens/e/B000AP9GV4/), в частности программ сетевого программирования, и прочитать их. – zwol

+0

Я протестировал его, а сервер и клиент подключились, и оба потока начинаются – kevman

+0

Это явно не работает, хотя, или вы не задали бы вопрос? – zwol

ответ

0

Это я просто затмил свою глобальную переменную, ведущую меня к , полагая, что я был связан, потому что он был в основном. но когда поток попытался получить доступ к глобальной переменной, он не был инициализирован. Теперь , почему это было вторя я понятия не имею,

Просто создалось впечатление, «это было эхо», потому что write(sock, bufferU, strlen(bufferU)) к инициализируется (т.е. нулевой инициализируется) глобальный sock не писал на сервер, а скорее к файловому дескриптору 0; в UNIX-подобных системах это обычно появляется на вашем терминале.

Смежные вопросы