QtのSocketクラスを使えば、その辺をラップしてくれて楽ができます。ちょっと試しにServerのconsoleプログラムを作ってみました。telnet等をclientにして簡単に実験ができます。
proファイルですが、networkの追加を忘れずに。
MyTcpServer.pro
QT -= gui QT += core QT += network CONFIG += c++11 console CONFIG -= app_bundle
mainは以下の通りです。
main.cpp
#include <qcoreapplication> #include "mytcpserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyTcpServer server; return a.exec(); }
MyTcpServerクラスのヘッダは最低限の関数だけ定義します。
mytcpserver.h
#ifndef MYTCPSERVER_H #define MYTCPSERVER_H #include <qobject> #include <qtcpsocket> #include <qtcpserver> #include <qdebug> class MyTcpServer : public QObject { Q_OBJECT public: explicit MyTcpServer(QObject *parent = 0); signals: public slots: void newConnection(); void readData(); void socketError(QAbstractSocket::SocketError); void deleteLater(); private: QTcpServer *server; QTcpSocket *socket; }; #endif // MYTCPSERVER_H
本体です。connectして文字列を送ったら、後は受信するだけのものです。相手がcloseしたり、勝手に切ったり、エラーが起きたらServer側はcloseするようにしてあります。また、"bye"を受信したらやはりcloseします。(windowsだと、受信文字列の最後にCR/LFが含まれますが、Linux等では行末記号は含まれてきません。)
mytcpserver.cpp
// mytcpserver.cpp #include "mytcpserver.h" #include <qdatastream .h> MyTcpServer::MyTcpServer(QObject *parent) : QObject(parent) { server = new QTcpServer(this); // 誰かがconnectしてきたら、signalを出してnewConnection()をCall connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); if(!server->listen(QHostAddress::Any, 9999)) { qDebug() << "Server could not start"; } else { qDebug() << "Server started!"; } } void MyTcpServer::newConnection() { // socketのインスタンスを生成 socket = server->nextPendingConnection(); qDebug() << " connect"; // 切断した時の処理を定義 connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); // 受信した時の処理を定義 connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); // エラー処理の定義 connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); // 切断したときの処理としてSLOT関数を設定する。 connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); // クライアントのIPアドレスを取得 QString ippaddr = socket->localAddress().toString(); // 送信データをstreamとして定義 QDataStream out(socket); out.setVersion(QDataStream::Qt_5_0); out << "Hello client\r\n"; } void MyTcpServer::readData() { QString s; // 受信データを」streamとして定義 //QDataStream in(socket); //in.setVersion(QDataStream::Qt_5_11); //in >> s; s = socket->readAll(); qDebug() << "receive : " << s << "( " << s.size() << " ) "; if ("bye\r\n" == s) { qDebug() << "close process"; //deleteLater(); emit socket->disconnected(); } } void MyTcpServer::socketError(QAbstractSocket::SocketError ) { qDebug() << " socket error!"; socket->close(); } void MyTcpServer::deleteLater() { qDebug() << " socket disconnected"; socket->close(); }
さて、ここでこのサンプルを作った本題に入ります。前からLinuxでSocket(TCP)のプログラムで悩んでいたのが、通信しているプログラムの片方が、socketをつないだまま、異常終了すると、しばらくその時のportが使えなくなる現象に困っていました。一方、昔winsockを見ていた時、windowsのAPIには、portを強制的にconnectするというフラグがあり、どう違うのか疑問に思っていました。
このサンプルプログラムで、windowsとLinux(or Mac)での挙動の違いを調べてみました。
(1)通信中にServerを強制終了すると、Linux(Mac)ではやはり当該portは使えなくなります。(already useのエラーメッセージがでます)しかし、windowsは何事もなかったようにconnectできます。
(2)Linux(Mac)でも、client側が強制終了しても、サンプルのServerでは相手との通信が切れたことを検知して、portをcloseするようにしてあるので、再度clientは接続できます。(これまでLinuxでうまくいかなかったのは、この処理を入れてなかったことに気づきました)
Windowsは「楽観的」というか「危険」というか、考え方がかなり違います。
0 件のコメント:
コメントを投稿