Qt:Qt实现Winsock网络编程—非阻塞模式下的简单远程控制的开发(WSAAsyncSelect)
Qt實現(xiàn)Winsock網(wǎng)絡(luò)編程—非阻塞模式下的簡單遠程控制的開發(fā)(WSAAsyncSelect)
前言
這邊博客應該是 Qt實現(xiàn)Winsock網(wǎng)絡(luò)編程—TCP服務端和客戶端通信(多線程) 的姐妹篇,上篇博客中的socket通信中所用的Windows api函數(shù) 都是阻塞函數(shù),而一般圖形界面編程中的UI進程一般不能阻塞,所以上篇博客 采用的是多線程,將接受連接請求 和 通信的socket處理都放在單獨的后臺線程中,這樣就不會造成界面無響應的情況。當然Windows api和Linux一樣也都提供了非阻塞模式的套接字開發(fā),這樣我們也就不需要多線程了,用單線程一樣的可以很好的處理。下面我們就來看看吧。
界面效果
主要是客戶端向服務端 發(fā)送一個命令,然后服務端進行相應的回應。當然要實現(xiàn) 多個客戶端 和 一個服務端通信的效果塞,因為為 非阻塞模式,就可以不用多線程了。當然swap和restore命令可以控制服務器做出改變的。代碼 沒有什么太難的,需要有qt基礎(chǔ)和查看MSDN的能力 即可實現(xiàn)。動手吧,編程 唯有實踐。
Windows 非阻塞模式設(shè)置
這里我們使用WSAAsyncSelect設(shè)置非阻塞模式的套接字,當然還有其他方式。WSAAsyncSelect是基于消息驅(qū)動機制的,在qt中通過重新nativeEvent來獲取消息。
int WSAAPI WSAAsyncSelect(SOCKET s, //指定要改變工作模式為非阻塞模式的套接字HWND hWnd, //發(fā)生網(wǎng)絡(luò)事件時接受消息的窗口u_int wMsg, //發(fā)生網(wǎng)絡(luò)事件時向窗口發(fā)送的消息,該消息為自定義消息long lEvent //指定應用程序感興趣的通知碼,可以是組合(FD_READ,FD_ACCEPT,FD_CONNECT,FD_CLOSE) );客戶端代碼
主要是nativeEvent函數(shù)的重寫和非阻塞模式的設(shè)置,最后面有 完整代碼下載路徑。
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result) {MSG* msg = (MSG*)message;switch(msg->message){case WM_SOCK://lParam的低位表示通知碼//這段代碼不明白,可以參考MSDN https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-connectswitch (WSAGETSELECTEVENT(msg->lParam)) {case FD_CONNECT:{//錯誤碼 返回在lParam的高位int error = WSAGETSELECTERROR(msg->lParam);if( WSAECONNREFUSED == error || WSAENETUNREACH == error ||WSAETIMEDOUT == error){//連接失敗ui->lineEdit->setEnabled(true);ui->pushButton->setEnabled(true);qDebug() << "連接服務器失敗";ui->listWidget->insertItem(0,QString("%1 連接服務器失敗").arg(getTimeString()));//放開編輯框和按鈕ui->lineEdit->setEnabled(true);ui->pushButton->setEnabled(true);}else{qDebug() << "連接服務器成功";ui->pushButton->setText("斷開連接");ui->pushButton->setEnabled(true);ui->lineEdit_2->setEnabled(true);ui->pushButton_2->setEnabled(true);ui->listWidget->insertItem(0,QString("%1 連接服務器成功").arg(getTimeString()));}}break;case FD_READ:qDebug() << "收到服務端回的消息";memset(mBuf,0,4096);//收到服務器發(fā)送的消息了recv(mSocket,mBuf,sizeof(mBuf),0);ui->listWidget->insertItem(0,QString("%1 recv:[%2]").arg(getTimeString()).arg(mBuf));break;case FD_CLOSE://服務端 套接字關(guān)閉了qDebug() << "服務端關(guān)閉了";ui->listWidget->insertItem(0,QString("%1 服務端斷開連接了").arg(getTimeString()));break;}break;}//其他交給qt處理return QWidget::nativeEvent(eventType, message, result); }連接服務器
void Widget::on_pushButton_clicked() {QString but = ui->pushButton->text();if(but.compare("斷開連接") == 0){//斷開連接qDebug() << "客戶端 主動斷開連接";closesocket(mSocket);//關(guān)閉套接字ui->listWidget->insertItem(0,QString("%1 客戶端主動斷開連接").arg(getTimeString()));ui->lineEdit->setEnabled(true);ui->lineEdit_2->setEnabled(false);ui->pushButton_2->setEnabled(false);ui->pushButton->setText("連接");return;}QString ip = ui->lineEdit->text();if(ip.isEmpty())return;ui->pushButton->setEnabled(false);ui->lineEdit->setEnabled(false);//創(chuàng)建連接套接字mSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//設(shè)置非阻塞模式,對 客戶端連接到服務器 和 收到消息 通知碼 感興趣WSAAsyncSelect(mSocket,(HWND)winId(),WM_SOCK,FD_CONNECT|FD_READ|FD_CLOSE);mAddr.sin_addr.S_un.S_addr = inet_addr(ip.toUtf8().data());mAddr.sin_family = AF_INET;mAddr.sin_port = htons(8888);::connect(mSocket,(sockaddr*)&mAddr,sizeof(mAddr)); }服務端代碼
nativeEvent函數(shù)
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result) {MSG* msg = (MSG*)message;switch(msg->message){case WM_SOCK://發(fā)生錯誤統(tǒng)一處理if(WSAGETSELECTERROR(msg->lParam)){qDebug() << "發(fā)生錯誤";break;}switch (WSAGETSELECTEVENT(msg->lParam)) {case FD_ACCEPT:{qDebug() << "有新的客戶端進行連接";sockaddr_in *addr = new sockaddr_in;//傳出參數(shù)int len;//傳出參數(shù)SOCKET client = accept(mListen,(sockaddr*)addr,&len);//通信套接字 對read和close感興趣WSAAsyncSelect(client,(HWND)winId(),WM_SOCK,FD_READ | FD_CLOSE);//給客戶端發(fā)送菜單信息char* ch = mHelp.toUtf8().data();send(client,ch,strlen(ch)+1,0);char* ipStr = inet_ntoa(addr->sin_addr);int port = ntohs(addr->sin_port);ui->listWidget->insertItem(0,QString("%1 [%2:%3] 連接成功").arg(this->getTimeString()).arg(QString(ipStr)).arg(port));//保存到全局對象中mClientMap.insert(client,addr);}break;case FD_READ:{memset(mBuf,0,4096);SOCKET client = (SOCKET)msg->wParam;sockaddr_in *addr = mClientMap.find(client).value();//msg->wParam保存的是發(fā)生網(wǎng)絡(luò)事件的套接字char* ipStr = inet_ntoa(addr->sin_addr);int port = ntohs(addr->sin_port);recv(client,mBuf,4096,0);dealCommand(client);ui->listWidget->insertItem(0,QString("%1 [%2:%3]:%4").arg(this->getTimeString()).arg(ipStr).arg(port).arg(mBuf));}break;case FD_CLOSE:qDebug() << "客戶端斷開連接";SOCKET client = (SOCKET)msg->wParam;sockaddr_in *addr = mClientMap.find(client).value();//msg->wParam保存的是發(fā)生網(wǎng)絡(luò)事件的套接字closesocket(client);mClientMap.remove(client);char* ipStr = inet_ntoa(addr->sin_addr);int port = ntohs(addr->sin_port);ui->listWidget->insertItem(0,QString("%1 [%2:%3] 斷開連接").arg(this->getTimeString()).arg(ipStr).arg(port));break;}}//其他交給qt處理return QWidget::nativeEvent(eventType, message, result); }處理客服端發(fā)送過來的命令
//處理 客戶端發(fā)送過來的命令 void Widget::dealCommand(SOCKET client){if(QString(mBuf).compare("help") == 0){char* ch = mHelp.toUtf8().data();send(client,ch,strlen(ch)+1,0);}else if(QString(mBuf).compare("swap") == 0){SwapMouseButton(true);char* ch = "swap命令執(zhí)行成功";send(client,ch,strlen(ch)+1,0);}else if(QString(mBuf).compare("restore") == 0 ){SwapMouseButton(false);char* ch = "restore命令執(zhí)行成功";send(client,ch,strlen(ch)+1,0);}else if(QString(mBuf).compare("getsysinfo") == 0){char buf[1024]{0};DWORD nsize;GetComputerNameA(buf,&nsize);QString info("\ncomputer name:%1\nuser name:%2\n");info = info.arg(QString(buf));memset(buf,0,1024);GetUserNameA(buf,&nsize);info = info.arg(QString(buf));char* ch = info.toUtf8().data();send(client,ch,strlen(ch)+1,0);}else{//未知命令 回射回去send(client,mBuf,strlen(mBuf)+1,0);}return; }完整代碼
完整代碼下載路徑或者GitHub最新代碼。
總結(jié)
以上是生活随笔為你收集整理的Qt:Qt实现Winsock网络编程—非阻塞模式下的简单远程控制的开发(WSAAsyncSelect)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++:Windows编程—Inli
- 下一篇: 15.IDA-查看XREF列表(Ctrl