伊莉討論區

標題: [已解決]在關掉thread後,無法使用getline讀取console上的資料 [打印本頁]

作者: opchta    時間: 2021-1-1 11:50 PM     標題: [已解決]在關掉thread後,無法使用getline讀取console上的資料

本帖最後由 opchta 於 2021-1-10 11:13 PM 編輯

各位大大好
小弟我在linux 下寫了一支server,
尤於有特殊需求,我需要一個thread專門處理client的訊息,
所以在程式一開始,我做完socket的初始化後,
就開了一個thread等待client連線,並處理連線後client傳來的訊息。

至於主執行緒的部分,我希望使用者可以下一些命令,
然後程式執行相對應的動作。
其中一項指令就是關掉監聽,
但關掉監聽後輸入任何指令都沒反應。

後來是發現在關掉監聽後,getline會一直讀取到空字串,
一開始以為是getline的問題,但後來改成fgets也是發生一樣的狀況。
如果不關掉監聽,輸入什麼都沒事

請問有大大知道為什麼會這樣嗎?
或是給我一個查資料的方向,我目前是查關掉thread造成iostream有問題,
但得到的答案好像都不是我要的。

下面是我的程式碼,最下面有程式碼相關的解說:
TcpServer.h
  1. #ifndef _SERVER_H_
  2. #define _SERVER_H_

  3. #include <unistd.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>
  7. #include <string.h>
  8. #include <functional>
  9. #include <atomic>
  10. #include <thread>

  11. class TcpServer
  12. {
  13.     public:
  14.         TcpServer();
  15.         ~TcpServer();
  16.         void StopListen();
  17.         void StartListen();
  18.         bool SendData(char* data, int len);
  19.         std::function<void(char*)> ReplyClientCmd = nullptr;

  20.     private:
  21.         char mServerIP[20];
  22.         int mPort = 0;
  23.         sockaddr_in mMasterAddr;
  24.         int mMasterFd = 0;
  25.         sockaddr_in mClient;
  26.         int mClientFd = 0;
  27.         // bool mIsListening = false;
  28.         std::atomic<bool> mIsListening;
  29.         bool Initialize();
  30.         void ListenFunction();
  31.         std::thread mListenThread;
  32. };

  33. #endif //_SERVER_H_
複製代碼


TcpServer.cpp
  1. #include "TcpServer.h"
  2. #include <iostream>

  3. TcpServer::TcpServer()
  4. {
  5.     strcpy(this->mServerIP, "192.168.0.120"); // it would be read from configure file, future.
  6.     this->mPort = 8080; // it would be read from configure file, future.

  7.     if(!Initialize())
  8.     {
  9.         std::cout << __func__ << " initialize failded!" << std::endl;
  10.         return;
  11.     }
  12. }

  13. TcpServer::~TcpServer()
  14. {
  15.     StopListen();
  16. }

  17. void TcpServer::StopListen()
  18. {
  19.     this->mIsListening = false;
  20.     close(this->mMasterFd);
  21.     close(this->mClientFd);
  22. }

  23. bool TcpServer::Initialize()
  24. {
  25.     this->mMasterFd = socket(AF_INET, SOCK_STREAM, 0);
  26.     if(this->mMasterFd == -1)
  27.     {
  28.         perror("Create socket failed"); // log
  29.         return false;
  30.     }

  31.     int opt = 1;
  32.     if(setsockopt(this->mMasterFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
  33.     {
  34.         perror("set socket error!"); // log
  35.         return false;
  36.     }

  37.     this->mMasterAddr.sin_family = AF_INET;
  38.     //this->mMasterAddr.sin_addr.s_addr = inet_addr(this->mServerIP);
  39.     this->mMasterAddr.sin_addr.s_addr = INADDR_ANY;
  40.     this->mMasterAddr.sin_port = htons(this->mPort);
  41.     return true;
  42. }

  43. void TcpServer::StartListen()
  44. {
  45.     if(!this->mIsListening) {
  46.         this->mIsListening = true;

  47.         mListenThread = std::thread(&TcpServer::ListenFunction, this); //開thread等待連線及監聽
  48.     }
  49.     else{
  50.         perror("Have a thread listening! Please STOP it!");
  51.         return;
  52.     }
  53. }

  54. void TcpServer::ListenFunction()
  55. {
  56.     int sockLen = sizeof(this->mMasterAddr);
  57.     if(bind(this->mMasterFd, (sockaddr *)&this->mMasterAddr, sockLen) < 0){
  58.         perror("Occuring ERROR in bind!");
  59.         this->mIsListening = false;
  60.         return;
  61.     }

  62.     if(listen(this->mMasterFd, 5) < 0){
  63.         perror("Occuring ERROR in listen!");
  64.         this->mIsListening = false;
  65.         return;

  66.     }

  67.     while(this->mIsListening){ //mIsListening是用來決定要不要繼續監聽的flag
  68.         std::cout << "waitting connection.........." << std::endl;
  69.         this->mClientFd = accept(this->mMasterFd, (sockaddr *)&this->mClient, (socklen_t *)&sockLen);
  70.         if(this->mClientFd < 0){
  71.             perror("accept connection failed!");
  72.             this->mIsListening = false;
  73.             return;
  74.         }
  75.         std::cout << "accept from " << inet_ntoa(this->mClient.sin_addr) << " connection" << std::endl;

  76.         while (this->mIsListening) { // 接收client的訊息
  77.             char buffer[1024];
  78.             memset((void *)buffer, 0, 1024);
  79.             int lenRecv = recv(this->mClientFd, buffer, 1024, 0);
  80.             if(lenRecv > 0){
  81.                 std::cout << "recieve package len = " << lenRecv << ", message = " << buffer << std::endl;
  82.                 // if connection success, here do some decoding or reply command.
  83.                 if(ReplyClientCmd != nullptr){
  84.                     ReplyClientCmd(buffer);
  85.                 }
  86.                 const char *retStr = "server receive!";
  87.                 send(this->mClientFd, retStr, strlen(retStr), 0);
  88.             }
  89.             else{
  90.                 std::cout << "connection break!\n" << std::endl;
  91.                 close(this->mClientFd);
  92.                 break;
  93.             }
  94.         }
  95.     }

  96. }

  97. bool TcpServer::SendData(char* data, int len)
  98. {
  99.     int ret = send(this->mClientFd, data, len, 0);
  100.     if(ret > 0)
  101.         return true;
  102.     else
  103.         return false;   
  104. }
複製代碼


main.cpp
  1. #include <iostream>
  2. #include "TcpServer.h"
  3. #include <memory>
  4. #include <string>

  5. int main(int argc, const char* argv[])
  6. {
  7.     TcpServer *server = new TcpServer();
  8.     server->StartListen();

  9.     std::string command;
  10.     for(;;){ //等待使用者輸入訊息並做相對應的動作
  11.         getline(std::cin, command);

  12.         if(command == std::string("CloseConnection"))
  13.             server->StopListen();
  14.         else{
  15.             std::cout << "type: " << command << std::endl;
  16.             // server->SendData((char*)command.c_str(), command.length());
  17.         }
  18.     }

  19.     return EXIT_SUCCESS;
  20. }
複製代碼


其中TcpServer是用來控制網路連線的,
TcpServer的ListenFunction就是用來等待client連線,
並監聽client訊息的方法。

在程式一開始會先new TcpServer出來,
在裡面會做相關的初始化,
接著程式會呼叫StartListen,
在StartListen中,我會將ListenFunction塞到一個thread中,
讓那個thread專門等待連線及監聽client。

最後有個無窮迴圈,
在等待使用者輸入指令,並做相對應的動作。


作者: tryit244178    時間: 2021-1-2 10:49 AM

本帖最後由 tryit244178 於 2021-1-2 12:57 PM 編輯

在第14行加個printf看有沒有抓到輸入的字串。如果有,也許在停止的時候,把TcpServer物件釋放掉了
作者: opchta    時間: 2021-1-2 06:20 PM

tryit244178 發表於 2021-1-2 10:49 AM
在第14行加個printf看有沒有抓到輸入的字串。如果有,也許在停止的時候,把TcpServer物件釋放 ...

我有做過這件事,在輸入CloseConnection前,輸入什麼,就會輸出什麼,
例如,輸入「yo~」,在螢幕上就會輸出「yo~」。

但輸出CloseConnection後,會先輸出「CloseConnection」這個字串,
接著就一直輸出空字串,
我打什麼都無法結束輸出空字串,
作者: baepi    時間: 2021-1-5 10:02 PM

本帖最後由 baepi 於 2021-1-5 10:04 PM 編輯

小弟電腦是Win10(不知道是不是這原因)
在稍作修改大大的程式碼之後,都是小修改...好比
1.刪掉你們有但是我們沒有的.H檔
2.你的初始化沒有
  1. WSAData wsaData;
  2.         WORD version = MAKEWORD(2, 2); // 版本
  3.         int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); // 成功回傳 0
  4.         if (iResult != 0) {
  5.                 // 初始化Winsock 失敗
  6.                 return false;
  7.         }
複製代碼
我們沒有這段根本不可能初始化成功
3.你們的中斷使用close.....我們是使用closesocket

總之能夠正常運行之後,我執行沒有發生大大所說的問題(CloseConnection太長了...所以我自己把它改成A了)
還是說我誤會了大大的意思??
[attach]134067886[/attach]

作者: cockroachrun    時間: 2021-1-8 09:25 AM

本帖最後由 cockroachrun 於 2021-1-8 09:29 AM 編輯

你把stdin 的file descriptors close 了阿. 當然讀不到東西.
你的 int mClientFd  初值為0 . 如果他沒有被改過. (沒有client 連上). 然後你的StopListen()
close(mClientFd);  FD = 0 剛好是 stdin 的 file descriptors . 就讀不到東西
附帶一提
FD = 0   stdin
FD = 1   stdout
FD = 2   stderr

可用 read,write 去操作. fgets, cin 通通都是透過 stdin 去讀取. 所以. close(0) 就不能讀取了

作者: opchta    時間: 2021-1-10 11:12 PM

cockroachrun 發表於 2021-1-8 09:25 AM
你把stdin 的file descriptors close 了阿. 當然讀不到東西.
你的 int mClientFd  初值為0 . 如果他沒有被 ...

喔喔~~~~瞭解
感謝大大的解答,將StopListen中的close(mClientFd)拿掉就真的都OK了
而且第一次知道FD有這些涵意,
學到了!





歡迎光臨 伊莉討論區 (http://a04.eyny.com/) Powered by Discuz!