2017-04-18 43 views
-1

要學習使用TCP的套接字編程,我正在製作一個簡單的服務器和客戶端。客戶端將發送文件塊,服務器將讀取它們並寫入文件。客戶端和服務器在沒有任何多處理的情況下正常工作。我想要讓多個客戶端可以同時連接。我想給每個連接的客戶端一個唯一的ID,稱爲「client_id」。這是1到n之間的數字。爲每個子進程分配一個帶有分叉的唯一ID

我試圖使用fork()以產生一個子進程,並在子進程中接受連接,然後讀取數據並將其保存到文件中。但是,client_id變量在進程中不同步,所以有時會增加,有時不會。我不完全理解發生了什麼。 client_id的值不應該重複,但有時我會看到數字出現兩次。我相信這是因爲在分叉過程中,子進程獲得了父進程的所有內容的副本,但並行進程之間沒有同步。

這是我的無限循環,坐在並等待連接客戶端。在子進程中,我在另一個無限循環中傳輸文件,當recv收到0字節時終止。

int client_id = 0; 
while(1){ 

     // accept a new connection 
     struct sockaddr_in clientAddr; 
     socklen_t clientAddrSize = sizeof(clientAddr); 

     //socket file descriptor to use for the connection 
     int clientSockfd = accept(sockfd, (struct sockaddr*)&clientAddr, &clientAddrSize); 
     if (clientSockfd == -1) { 
      perror("accept"); 
      return 4; 
     } 
     else{ //handle forking 
      client_id++; 
      std::cout<<"Client id: "<<client_id<<std::endl; 

      pid_t pid = fork(); 
      if(pid == 0){ 
       //child process 
       std::string client_idstr = std::to_string(client_id); 

       char ipstr[INET_ADDRSTRLEN] = {'\0'}; 
       inet_ntop(clientAddr.sin_family, &clientAddr.sin_addr, ipstr, sizeof(ipstr)); 

       std::string connection_id = std::to_string(ntohs(clientAddr.sin_port)); 
       std::cout << "Accept a connection from: " << ipstr << ":" << client_idstr 
       << std::endl; 

       // read/write data from/into the connection 
       char buf[S_BUFSIZE] = {0}; 
       std::stringstream ss; 

       //Create file stream 
       std::ofstream file_to_save; 

       FILE *pFile; 

       std::string write_dir = filedir+"/" + client_idstr + ".file"; 

       std::string write_type = "wb"; 
       pFile = fopen(write_dir.c_str(), write_type.c_str()); 
       std::cout<<"write dir: "<<write_dir<<std::endl; 


       while (1) { 
        memset(buf, '\0', sizeof(buf)); 

        int rec_value = recv(clientSockfd, buf, S_BUFSIZE, 0); 

        if (rec_value == -1) { 
         perror("recv"); 
         return 5; 
        }else if(rec_value == 0){ 
         //end of transmission, exit the loop 
         break; 
        } 


        fwrite(buf, sizeof(char), rec_value, pFile); 

       } 
       fclose(pFile); 
       close(clientSockfd); 
      } 
      else if(pid > 0){ 
       //parent process 
       continue; 
      }else{ 
       perror("failed to create multiple new threads"); 
       exit(-1); 
      } 
     } 

    } 

這裏是服務器的輸出,當我做以下,並在括號中預期的文件名(client_id.file):

1)連接的客戶端1,傳輸文件,斷開客戶機1(1。文件)
2)連接的客戶端2,傳輸文件中,斷開客戶端2(2.文件)
3)連接的客戶端1,傳輸文件中,斷開的客戶端1(3.file)
4)連接的客戶端1,傳輸文件,斷開客戶端1(4.file)
5)連接客戶端2,傳輸文件,disconnectc牛逼客戶端2(5.file)
6)連接的客戶端2,傳輸文件,斷開客戶端2(6.file)

enter image description here

+1

此'std :: cout <<'語法不是C,將標記更改爲C++ –

+1

不會使用std :: string,std :: ostream等指示更多的C++。程序?另外進程與線程不一樣 – infixed

+0

不確定這是否是原因,但是您應該在子進程中關閉(sockfd)。 – dbush

回答

0

我可能是錯的,但一些發癢這個計數器辦法我的記憶。你的計數器是如何定義的,它是一個局部變量嗎?它應該有整個線程的生命週期,所以無論是全局變量還是靜態本地,fork都會在複製內存頁時將其值複製到衍生進程中。存儲爲臨時的局部變量以及在C++中發生的局部變量未定義。 如果你需要共享變量,還有辦法,通過使用共享內存.. mmap等等。

其次..它是一個進程的副本,它將從調用fork()的位置開始執行。你在那裏?無限循環結束。循環將重新啓動。你應該從它退出,如果PID爲0

有例如內for()循環斐波納契原則繁殖過程: Visually what happens to fork() in a For Loop

  1. 父增加從0計數器1和叫叉。現在我們有孩子,打印1.
  2. CHILD完成傳輸並返回到循環開始。它在收到連接時反向增加到2。父母在設法接收連接時增加了兩個。現在你有4個進程。

如果您按順序進行了更多測試,您會看到重複數量上升。如果你看過那些數據,你實際上可以在你的taskmanager或top,ps輸出中看到這些進程。

+0

它被定義在外while循環的正上方。 – jonnyd42

+0

@ jonnyd42然後給它加靜態,應該夠了 – Swift

+0

不幸的是沒有解決問題。 – jonnyd42

相關問題