Chinaunix首页 | 论坛 | 博客
  • 博客访问: 38728
  • 博文数量: 20
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 145
  • 用 户 组: 普通用户
  • 注册时间: 2016-03-25 09:44
个人简介

unix

文章分类

全部博文(20)

文章存档

2016年(20)

我的朋友

分类: 嵌入式

2016-05-16 19:14:29

转自:http://blog.csdn.net/demowolf/archive/2010/05/17/5598879.aspx


折腾了很久TCP IP通信机制。


以前虽然看过bsd tcp/ip的so called 基础通信代码。什么bind, listen ,accept , receive, write, read,但是一直没真正理解。


这次由于公司需求,我狠狠地读了代码,并且搬出QT老本行,开始了QT For windows的编程。


这个大体构架是做一个聊天室软件。


每个client都可以给服务器TCP发消息,服务器通过TCP给各个客户端转发消息。


服务器端代码:


Server.cpp   继承 QTcpServer  主要用来listen to some port , 侦听到端口后, 继承重写了incomingConnection函数,来new 如下的一个


代码


tcpClientSocket.cpp 这个继承QTcpSocket ,用来 server.cpp里被 New 出来,接受各种请求


它重写了函数dataReceived , 即各种客户端发来的请求数据,(注意,这个不是第一步的connect状态,这个是业务逻辑上得请求,比如我给


server发送了“你好” ) 。


这一步处理好后,便开始给各个客户端分发同样的消息“你好” 。使用方法,很简单,QTcpSocket的write方法即可。


这里的细节重点是,在server.cpp里,每个new出来的TcpClientSocket的指针,我放到一个QLIST< TcpClientSocket * >模板里。这样,只要


你不删去这个节点,这个TCP链接就一直存在,嘿嘿,神奇吧。


刚开始我看QT自带example ,fortuneclient and threaded fortune server;我试图着在example的基础上修改代码,一步步达到目的。结果


发现他的业务逻辑,总是write后就自动disconnected, 我以为不disconnected,就能长链接,结果总是出错。


我一直纳闷,这是为什么呢?我用了个List保存了socket的descriptor,以为留着套接字的描述符,就可以下次再调出来用用。实际呢,必须


创建链路的时刻,就保存指针。TCP链接,指针在,链路在。指针亡,链路亡。


这也验证了我的想法,所谓一个真正的通信链路SOCKET的创建,是这样执行下去的。在APP层,我们调用了connect,实际OS对网卡发送了连接


对方的信号,这个电子,一路走过去,直到accept , 这一个链路创建了,在网卡开辟了区域了,在系统OS也开辟了内存,两方都为此一直保持


着这段数据的存在,指针即维系一个网络TCP链路的关键。


这就意味着,客户端无需写什么侦听代码来接受服务器端的消息,直接保持那个链路,消息自然就可以发过来,触发dataReceived信号。


写完代码后,我测试了一下,3个客户端同时链接TCP服务器端的5566端口,全部成功。


曾经很纠结我的所谓端口只能被一个占用。看来,理论远不如实际来的直接。


最后,我还是贴个代码吧。我知道,当一个人寻找各类消息的时候,代码总是最先看得,谁喜欢看人家博客唠叨半天,不讲大道理啊!


服务器端:

  1. chatserver.h  
  2. *********************  
  3. #ifndef CHATSERVER_H  
  4. #define CHATSERVER_H  
  5. #include   
  6. #include   
  7. #include "tcpclientsocket.h"  
  8. class ChatServer : public QTcpServer  
  9. {  
  10.     Q_OBJECT  
  11. public:  
  12.     ChatServer(QObject *parent = 0,int port=0);  
  13.     void PushMessage();  
  14.     QList tcpClientSocketList;  
  15. signals:  
  16.     void updateServer(QString,int);  
  17. public slots:  
  18.     void updateClients(QString,int);  
  19.     void slotDisconnected(int);  
  20. protected:  
  21.     void incomingConnection(int socketDescriptor);  
  22. private:  
  23.     QStringList fortunes;  
  24.     int onlineDescriptor;  
  25. };  
  26. #endif // CHATSERVER_H  
  27. *********************  
  28. chatserver.cpp  
  29. *********************  
  30. #include "chatserver.h"  
  31. #include "chatthread.h"  
  32. #include   
  33. ChatServer::ChatServer(QObject *parent,int port)  
  34.         : QTcpServer(parent)  
  35. {  
  36.     fortunes << tr("Searching for people...")  
  37.              << tr("You've find a people. Try say hello!")  
  38.              << tr("You've disconnected.");  
  39.     listen(QHostAddress::Any,port);  
  40. }  
  41. void ChatServer::incomingConnection(int socketDescriptor)  
  42. {  
  43.     TcpClientSocket *tcpClientSocket=new TcpClientSocket(this);  
  44.     connect(tcpClientSocket,SIGNAL(updateClients(QString,int)),this,SLOT(updateClients(QString,int)));  
  45.     connect(tcpClientSocket,SIGNAL(disconnect(int)),this,SLOT(slotDisconnected(int)));  
  46.     tcpClientSocket->setSocketDescriptor(socketDescriptor);  
  47.     tcpClientSocketList.append(tcpClientSocket);  
  48. /* 
  49.     onlineDescriptor=socketDescriptor; 
  50.     QString fortune = fortunes.at(qrand() % fortunes.size()); 
  51. //    QString fortune = fortunes.at(0); 
  52.     ChatThread *thread = new ChatThread(socketDescriptor, fortune, this); 
  53.     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); 
  54.     thread->start(); 
  55. */  
  56. }  
  57. void ChatServer::updateClients(QString msg, int length)  
  58. {  
  59.  //   emit updateServer(msg,length);  
  60.     for(int i=0;i
  61.     {  
  62.         QTcpSocket *item=tcpClientSocketList.at(i);  
  63.         if(item->write(msg.toLatin1(),length)!=length)  
  64.         {  
  65.             continue;  
  66.         }  
  67.     }  
  68. }  
  69. void ChatServer::slotDisconnected(int descriptor)  
  70. {  
  71.     for (int i=0;i
  72.     {  
  73.         QTcpSocket *item=tcpClientSocketList.at(i);  
  74.         if(item->socketDescriptor()==descriptor)  
  75.         {  
  76.             tcpClientSocketList.removeAt(i);  
  77.             return;  
  78.         }  
  79.     }  
  80.     return;  
  81. }  
  82. void ChatServer::PushMessage()  
  83. {  
  84.     QString testString;  
  85.     testString="fuck u";  
  86.     updateClients(testString,testString.length());  
  87.  /* 
  88.    ChatThread *thread = new ChatThread(onlineDescriptor, testString, this); 
  89.     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); 
  90.     thread->start(); 
  91. */  
  92. }  
  93. *********************  
  94. chatui.h  
  95. *********************  
  96. #ifndef CHATUI_H  
  97. #define CHATUI_H  
  98. #include   
  99. #include "chatserver.h"  
  100. QT_BEGIN_NAMESPACE  
  101. class QLabel;  
  102. class QPushButton;  
  103. QT_END_NAMESPACE  
  104. class ChatUi : public QDialog  
  105. {  
  106.     Q_OBJECT  
  107. public:  
  108.     ChatUi(QWidget *parent = 0);  
  109. private slots:  
  110.     void Test();  
  111. private:  
  112.     QLabel *statusLabel;  
  113.     QPushButton *quitButton;  
  114.     QPushButton *testButton;  
  115.     ChatServer *server;  
  116. };  
  117. #endif // CHATUI_H  
  118. *********************  
  119. chatui.cpp  
  120. *********************  
  121. #include "chatui.h"  
  122. #include "chatserver.h"  
  123. #include   
  124. #include   
  125. #include   
  126. ChatUi::ChatUi(QWidget *parent)  
  127.     :QDialog(parent)  
  128. {  
  129.     statusLabel = new QLabel;  
  130.     quitButton=new QPushButton(tr("Quit"));  
  131.     quitButton->setAutoDefault(false);  
  132.     testButton=new QPushButton(tr("Test"));  
  133.     testButton->setAutoDefault(false);  
  134.     server=new ChatServer(this,5566);  
  135.     if (!server->isListening()) {  
  136.         QMessageBox::critical(this, tr("Chat Server"),  
  137.                               tr("Unable to start the server: %1.")  
  138.                               .arg(server->errorString()));  
  139.         close();  
  140.         return;  
  141.     }  
  142.     QString ipAddress;  
  143.     QList ipAddressesList = QNetworkInterface::allAddresses();  
  144.     for (int i = 0; i < ipAddressesList.size(); ++i) {  
  145.             if (ipAddressesList.at(i) != QHostAddress::LocalHost &&  
  146.                 ipAddressesList.at(i).toIPv4Address()) {  
  147.                 ipAddress = ipAddressesList.at(i).toString();  
  148.                 break;  
  149.             }  
  150.         }  
  151.     if (ipAddress.isEmpty())  
  152.             ipAddress = QHostAddress(QHostAddress::LocalHost).toString();  
  153.     statusLabel->setText(tr("The server is running on/n/nIP: %1/nport: %2/n/n"  
  154.                                 "Run the Fortune Client example now.")  
  155.                              .arg(ipAddress).arg(server->serverPort()));  
  156.     connect(quitButton,SIGNAL(clicked()),this,SLOT(close()));  
  157.     connect(testButton,SIGNAL(clicked()),this,SLOT(Test()));  
  158.     QHBoxLayout *buttonLayout = new QHBoxLayout;  
  159.     buttonLayout->addStretch(1);  
  160.     buttonLayout->addWidget(quitButton);  
  161.     buttonLayout->addStretch(1);  
  162.     buttonLayout->addWidget(testButton);  
  163.     buttonLayout->addStretch(1);  
  164.     QVBoxLayout *mainLayout = new QVBoxLayout;  
  165.     mainLayout->addWidget(statusLabel);  
  166.     mainLayout->addLayout(buttonLayout);  
  167.     setLayout(mainLayout);  
  168.     setWindowTitle(tr("Chat Server"));  
  169. }  
  170. void ChatUi::Test()  
  171. {  
  172.     server->PushMessage();  
  173. }  
  174. *********************  
  175. tcpclientsocket.h  
  176. *********************  
  177. #ifndef TCPCLIENTSOCKET_H  
  178. #define TCPCLIENTSOCKET_H  
  179. #include   
  180. class TcpClientSocket : public QTcpSocket  
  181. {  
  182.     Q_OBJECT  
  183. public:  
  184.     TcpClientSocket(QObject* parent=0);  
  185.     ~TcpClientSocket();  
  186. signals:  
  187.     void updateClients(QString,int);  
  188.     void disconnected(int);  
  189. protected slots:  
  190.     void dataReceived();  
  191. //    void slotDisconnected();  
  192. };  
  193. #endif // TCPCLIENTSOCKET_H  
  194. *********************  
  195. tcpclientsocket.cpp  
  196. *********************  
  197. #include "tcpclientsocket.h"  
  198. TcpClientSocket::TcpClientSocket(QObject* parent)  
  199. {  
  200.     connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived()));  
  201.     connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));  
  202. }  
  203. TcpClientSocket::~TcpClientSocket()  
  204. {  
  205. }  
  206. void TcpClientSocket::dataReceived()  
  207. {  
  208.     while(bytesAvailable()>0)  
  209.     {  
  210.         char buf[1024];  
  211.         int length=bytesAvailable();  
  212.         read(buf,length);  
  213.         QString msg=buf;  
  214.         emit updateClients(msg,length);  
  215.     }  
  216. }  
  217. *********************  
  218. 下面是客户端代码  
  219. client.h  
  220. *********************  
  221.   
  222. #ifndef CLIENT_H  
  223. #define CLIENT_H  
  224. #include   
  225. #include   
  226. QT_BEGIN_NAMESPACE  
  227. class QDialogButtonBox;  
  228. class QLabel;  
  229. class QLineEdit;  
  230. class QPushButton;  
  231. class QTcpSocket;  
  232. QT_END_NAMESPACE  
  233. //! [0]  
  234. class Client : public QDialog  
  235. {  
  236.     Q_OBJECT  
  237. public:  
  238.     Client(QWidget *parent = 0);  
  239. private slots:  
  240.     void requestNewFortune();  
  241.     void readFortune();  
  242.     void displayError(QAbstractSocket::SocketError socketError);  
  243.     void enableGetFortuneButton();  
  244.     void slotConnected();  
  245. private:  
  246.     QLabel *hostLabel;  
  247.     QLabel *portLabel;  
  248.     QLineEdit *hostLineEdit;  
  249.     QLineEdit *portLineEdit;  
  250.     QLabel *statusLabel;  
  251.     QPushButton *getFortuneButton;  
  252.     QPushButton *quitButton;  
  253.     QDialogButtonBox *buttonBox;  
  254.     QTcpSocket *tcpSocket;  
  255.     QString currentFortune;  
  256.     quint16 blockSize;  
  257. #ifdef Q_OS_SYMBIAN  
  258.     bool isDefaultIapSet;  
  259. #endif  
  260. };  
  261. //! [0]  
  262. #endif  
  263. *********************  
  264. client.cpp  
  265. *********************  
  266.   
  267. #include   
  268. #include   
  269. #include "client.h"  
  270. #ifdef Q_OS_SYMBIAN  
  271. #include "sym_iap_util.h"  
  272. #endif  
  273. //! [0]  
  274. Client::Client(QWidget *parent)  
  275.     : QDialog(parent)  
  276. {  
  277. //! [0]  
  278.     hostLabel = new QLabel(tr("&Server name:"));  
  279.     portLabel = new QLabel(tr("S&erver port:"));  
  280.     // find out which IP to connect to  
  281.     QString ipAddress;  
  282.     QList ipAddressesList = QNetworkInterface::allAddresses();  
  283.     // use the first non-localhost IPv4 address  
  284.     for (int i = 0; i < ipAddressesList.size(); ++i) {  
  285.         if (ipAddressesList.at(i) != QHostAddress::LocalHost &&  
  286.             ipAddressesList.at(i).toIPv4Address()) {  
  287.             ipAddress = ipAddressesList.at(i).toString();  
  288.             break;  
  289.         }  
  290.     }  
  291.     // if we did not find one, use IPv4 localhost  
  292.     if (ipAddress.isEmpty())  
  293.         ipAddress = QHostAddress(QHostAddress::LocalHost).toString();  
  294.     hostLineEdit = new QLineEdit(ipAddress);  
  295.     portLineEdit = new QLineEdit;  
  296.     portLineEdit->setValidator(new QIntValidator(1, 65535, this));  
  297.     hostLabel->setBuddy(hostLineEdit);  
  298.     portLabel->setBuddy(portLineEdit);  
  299.     statusLabel = new QLabel(tr("This examples requires that you run the "  
  300.                                 "Fortune Server example as well."));  
  301.     getFortuneButton = new QPushButton(tr("Get Fortune"));  
  302.     getFortuneButton->setDefault(true);  
  303.     getFortuneButton->setEnabled(false);  
  304.     quitButton = new QPushButton(tr("Quit"));  
  305.     buttonBox = new QDialogButtonBox;  
  306.     buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);  
  307.     buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);  
  308. //! [1]  
  309.     tcpSocket = new QTcpSocket(this);  
  310. //! [1]  
  311.     connect(hostLineEdit, SIGNAL(textChanged(QString)),  
  312.             this, SLOT(enableGetFortuneButton()));  
  313.     connect(portLineEdit, SIGNAL(textChanged(QString)),  
  314.             this, SLOT(enableGetFortuneButton()));  
  315.     connect(getFortuneButton, SIGNAL(clicked()),  
  316.             this, SLOT(requestNewFortune()));  
  317.     connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));  
  318. //! [2] //! [3]  
  319.     connect(tcpSocket,SIGNAL(connected()),this,SLOT(slotConnected()));  
  320.     connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readFortune()));  
  321. //! [2] //! [4]  
  322.     connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),  
  323. //! [3]  
  324.             this, SLOT(displayError(QAbstractSocket::SocketError)));  
  325. //! [4]  
  326.     QGridLayout *mainLayout = new QGridLayout;  
  327.     mainLayout->addWidget(hostLabel, 0, 0);  
  328.     mainLayout->addWidget(hostLineEdit, 0, 1);  
  329.     mainLayout->addWidget(portLabel, 1, 0);  
  330.     mainLayout->addWidget(portLineEdit, 1, 1);  
  331.     mainLayout->addWidget(statusLabel, 2, 0, 1, 2);  
  332.     mainLayout->addWidget(buttonBox, 3, 0, 1, 2);  
  333.     setLayout(mainLayout);  
  334.     setWindowTitle(tr("Fortune Client"));  
  335.     portLineEdit->setFocus();  
  336. #ifdef Q_OS_SYMBIAN  
  337.     isDefaultIapSet = false;  
  338. #endif  
  339. //! [5]  
  340. }  
  341. //! [5]  
  342. //! [6]  
  343. void Client::requestNewFortune()  
  344. {  
  345.     getFortuneButton->setEnabled(false);  
  346. #ifdef Q_OS_SYMBIAN  
  347.     if(!isDefaultIapSet) {  
  348.         qt_SetDefaultIap();  
  349.         isDefaultIapSet = true;  
  350.     }  
  351. #endif  
  352.     blockSize = 0;  
  353.     tcpSocket->abort();  
  354. //! [7]  
  355.     tcpSocket->connectToHost(hostLineEdit->text(),  
  356.                              portLineEdit->text().toInt());  
  357. //! [7]  
  358. }  
  359. //! [6]  
  360. //! [8]  
  361. void Client::readFortune()  
  362. {  
  363.     while(tcpSocket->bytesAvailable()>0)  
  364.     {  
  365.         QByteArray datagram;  
  366.         datagram.resize(tcpSocket->bytesAvailable());  
  367.         tcpSocket->read(datagram.data(),datagram.size());  
  368.         QString msg=datagram.data();  
  369.         statusLabel->setText(msg);  
  370.         getFortuneButton->setEnabled(true);  
  371.     }  
  372.     /* 
  373. //! [9] 
  374.     QDataStream in(tcpSocket); 
  375.     in.setVersion(QDataStream::Qt_4_0); 
  376.     if (blockSize == 0) { 
  377.         if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) 
  378.             return; 
  379.         in >> blockSize; 
  380.     } 
  381.     if (tcpSocket->bytesAvailable() < blockSize) 
  382.         return; 
  383.     QString nextFortune; 
  384.     in >> nextFortune; 
  385. //! [12] 
  386.     currentFortune = nextFortune; 
  387. //! [9] 
  388.     statusLabel->setText(currentFortune); 
  389.     getFortuneButton->setEnabled(true); 
  390. */  
  391. }  
  392. //! [12]  
  393. //! [13]  
  394. void Client::displayError(QAbstractSocket::SocketError socketError)  
  395. {  
  396.     switch (socketError) {  
  397.     case QAbstractSocket::RemoteHostClosedError:  
  398.         QMessageBox::information(this, tr("Fortune Client"),  
  399.                                  tr("Romote server closed illegally."));  
  400.         break;  
  401.     case QAbstractSocket::HostNotFoundError:  
  402.         QMessageBox::information(this, tr("Fortune Client"),  
  403.                                  tr("The host was not found. Please check the "  
  404.                                     "host name and port settings."));  
  405.         break;  
  406.     case QAbstractSocket::ConnectionRefusedError:  
  407.         QMessageBox::information(this, tr("Fortune Client"),  
  408.                                  tr("The connection was refused by the peer. "  
  409.                                     "Make sure the fortune server is running, "  
  410.                                     "and check that the host name and port "  
  411.                                     "settings are correct."));  
  412.         break;  
  413.     default:  
  414.         QMessageBox::information(this, tr("Fortune Client"),  
  415.                                  tr("The following error occurred: %1.")  
  416.                                  .arg(tcpSocket->errorString()));  
  417.     }  
  418.     getFortuneButton->setEnabled(true);  
  419. }  
  420. //! [13]  
  421. void Client::enableGetFortuneButton()  
  422. {  
  423.     getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty()  
  424.                                  && !portLineEdit->text().isEmpty());  
  425. }  
  426. void Client::slotConnected()  
  427. {  
  428.     QString msg="hello,server";  
  429.     tcpSocket->write(msg.toLatin1(),msg.length());  
  430.     return;  
  431. }  
  432. *********************  
阅读(2287) | 评论(0) | 转发(0) |
0

上一篇:ubuntu串口使用

下一篇:perror()函数

给主人留下些什么吧!~~