3

C++ で簡単なチャット アプリケーションを作成しようとしています。それは機能しますが、他の誰かが入力している間に誰かが何かを入力した場合。彼らが入力していたものを上書きするようなものです。以下に例として写真をリンクしました。

クライアントとサーバーに使用しているコードは、次の場所にあります。

クライアント

サーバ

ピクチャー:

ビフォー ここに画像の説明を入力 アフター ここに画像の説明を入力

4

3 に答える 3

7

1 つの方法は、ユーザーが最初にテキスト行を入力し始めたときにフラグを設定することです。フラグが設定されている間にネットワークからデータを受信した場合は、受信したデータをローカル データ構造のキューに入れるだけです (つまり、ローカル ユーザーが Return キーを押すまで、まだ印刷されていません)。ローカル ユーザーが Return キーを押すと、入力中にキューに入れていたすべてのデータを出力し、フラグの設定を解除します。

もちろん、このアプローチにはいくつかの欠点があります。

  • ローカル ユーザーがテキストを入力し、Return キーを押さない場合、受信したリモート テキストは表示されません。これは、ユーザーがスペースを押して立ち去った場合などに問題になる可能性があります。
  • C/C++ の cin/stdin 機能は通常、行単位で動作します。ローカル ユーザーが文字を入力したときに端末を raw/non-canonical モードに設定してレポートを取得する必要があります。ローカルユーザーがリターンを押すまで文字をバッファリングし、テキスト行全体を一度にプログラムに報告します)

もう 1 つの方法は、ローカル ユーザーのテキストとリモート ユーザーのテキストを物理的に別の領域 (たとえば、多くのチャット プログラムのようにウィンドウの上半分と下半分) に保持することです。これを行うには、通常の C/C++ stdin/stdout/cin/cout API よりも多くの制御が必要になります。2 つの別個のテキスト ウィジェットを使用して (Win32 または Qt またはその他の GUI API を使用して) GUI ウィンドウを作成するか、すべてを MS-DOS ウィンドウ内に保持したい場合は、PDCursesなどを使用して実装する必要があります。それ。

ただし、これらのオプションはいずれも重要であり、チャット アプリケーションの他の部分よりも実装に多くの時間と労力がかかる可能性があります。それが私で、チャット アプリケーションが単なる学習課題である場合、現在の動作を単に「既知の制限」として文書化し、それを修正することを心配する必要はありません。

于 2012-12-16T09:04:02.793 に答える
2

Jeremy の投稿はかなり徹底していました。利用可能な別のオプションを追加します。入力中のユーザーが入力した文字を、最後に ENTER を押すまで追跡します。そうすれば、リモート ユーザーがテキストを入力したときに、あなたがしなければならないことは次のとおりです。

適切な数のバックスペース文字 ('\b') を端末に書き込み (つまり、ローカル ユーザーが入力したテキストの長さと同じ数)、新しい受信テキスト行を出力し、次にすべての文字をローカルに出力します。ユーザーは以前に入力しました。その後、通常どおり続行します。

新しい着信テキストが所定の位置に「スライド」したように見えます。

于 2012-12-18T19:41:56.217 に答える
1
#include <Conio.h>
#include <mutex> // C++11, if not C++11 find your own mutex

std::vector<char> inputBuffer;
std::mutex inputGuard;
struct Locker {
  std::mutex* m;
  Locker(std::mutex& m_):m(&m_) { m->lock(); }
  void release() { if (m) m->unlock(); m = 0; }
  ~Locker() { release(); }
}
void AddToInputBuffer( char c ) {
  Locker lock(inputGuard);
  inputBuffer.push_back(c);
  printf("%c",c);
}
std::string FinishInputBuffer() {
  Locker lock(inputGuard);
  std::string retval( inputBuffer.begin(), inputBuffer.end() );
  inputBuffer.clear();
  printf("\n");
  return retval;
}
void OverlappedPrintln( std::string s ) {
  Locker lock(inputGuard);
  for (int i = 0; i < inputBuffer.size(); ++i) {
    printf("%c", 8 /*backspace*/);
  }
  printf("%s\n", s.c_str());
  for (int i = 0; i < inputBuffer.size(); ++i) {
    printf("%c", inputBuffer[i]);
  }
}

std::string readCharactersFromUser() {
  // TODO: Handle exiting
  while( int input = getch() ) {
    if (input == '\n') {
      return FinishInputBuffer();
    AddToInputBuffer(input);
  }
}

ここで、 に置き換えcin.getline(message, 256);てから、その前にstd::string message = readCharactersFromuser()の長さを確認してください。string(message[0] == 's' && message[1] == 'a' && message[2] == 'y' && message[3] == ' '

プログラムの他の場所でprintf、代わりに a をビルドしstd::stringて呼び出しますOverlappedPrintln

これの効果は、何かを出力したいときは、ミューテックスをロックし、ユーザー入力の上にバックスペースを置き、メッセージを出力し、改行を行い、バックスペースしたテキストを出力して、ミューテックスを解放することです。ミューテックスは 99% の状況では必要ありませんが、特に最近のコンピューターの速度では必要ありませんが、良い習慣です。

上記のコードはテストもコンパイルもされていません。また、最高品質のものでもありません。

エコーしない get 文字を選択したのは、ロック中のユーザー入力から読み取ることができず、非同期で出力にエコーするとテキストが文字化けする可能性があるためです。したがって、エコーなしで読み取り、ミューテックス内でエコーinputBufferします。これにより、ユーザーが入力した文字と画面にエコーした文字が常に含まれるようになります。

これは @NikBougalis の回答の実装です。

于 2012-12-24T04:05:51.903 に答える