Qt实现Winsock网络编程—非阻塞模式下的简单远程控制的开发(WSAAsyncSelect)

前言

这边博客应该是 Qt实现Winsock网络编程—TCP服务端和客户端通信(多线程) 的姐妹篇,上篇博客中的socket通信中所用的Windows api函数 都是阻塞函数,而一般图形界面编程中的UI进程一般不能阻塞,所以上篇博客 采用的是多线程,将接受连接请求 和 通信的socket处理都放在单独的后台线程中,这样就不会造成界面无响应的情况。当然Windows api和Linux一样也都提供了非阻塞模式的套接字开发,这样我们也就不需要多线程了,用单线程一样的可以很好的处理。下面我们就来看看吧。

界面效果

主要是客户端向服务端 发送一个命令,然后服务端进行相应的回应。当然要实现 多个客户端 和 一个服务端通信的效果塞,因为为 非阻塞模式,就可以不用多线程了。当然swap和restore命令可以控制服务器做出改变的。代码 没有什么太难的,需要有qt基础和查看MSDN的能力 即可实现。动手吧,编程 唯有实践。

Windows 非阻塞模式设置

这里我们使用WSAAsyncSelect设置非阻塞模式的套接字,当然还有其他方式。WSAAsyncSelect是基于消息驱动机制的,在qt中通过重新nativeEvent来获取消息。

int WSAAPI WSAAsyncSelect(SOCKET s,  //指定要改变工作模式为非阻塞模式的套接字HWND   hWnd,   //发生网络事件时接受消息的窗口u_int  wMsg,    //发生网络事件时向窗口发送的消息,该消息为自定义消息long   lEvent //指定应用程序感兴趣的通知码,可以是组合(FD_READ,FD_ACCEPT,FD_CONNECT,FD_CLOSE)
);

客户端代码

主要是nativeEvent函数的重写和非阻塞模式的设置,最后面有 完整代码下载路径。

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);//收到服务器发送的消息了recv(mSocket,mBuf,sizeof(mBuf),0);ui->listWidget->insertItem(0,QString("%1 recv:[%2]").arg(getTimeString()).arg(mBuf));break;case FD_CLOSE://服务端 套接字关闭了qDebug() << "服务端关闭了";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);//关闭套接字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);//创建连接套接字mSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//设置非阻塞模式,对 客户端连接到服务器 和 收到消息 通知码 感兴趣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函数

bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{MSG* msg = (MSG*)message;switch(msg->message){case WM_SOCK://发生错误统一处理if(WSAGETSELECTERROR(msg->lParam)){qDebug() << "发生错误";break;}switch (WSAGETSELECTEVENT(msg->lParam)) {case FD_ACCEPT:{qDebug() << "有新的客户端进行连接";sockaddr_in  *addr = new sockaddr_in;//传出参数int len;//传出参数SOCKET client =  accept(mListen,(sockaddr*)addr,&len);//通信套接字 对read和close感兴趣WSAAsyncSelect(client,(HWND)winId(),WM_SOCK,FD_READ | FD_CLOSE);//给客户端发送菜单信息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保存的是发生网络事件的套接字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保存的是发生网络事件的套接字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);
}

处理客服端发送过来的命令

//处理 客户端发送过来的命令
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命令执行成功";send(client,ch,strlen(ch)+1,0);}else if(QString(mBuf).compare("restore") == 0 ){SwapMouseButton(false);char* ch = "restore命令执行成功";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最新代码。

Qt:Qt实现Winsock网络编程—非阻塞模式下的简单远程控制的开发(WSAAsyncSelect)相关推荐

  1. connect函数在阻塞和非阻塞模式下的行为

    connect函数在阻塞和非阻塞模式下的行为 当socket使用阻塞模式时,connect函数会阻塞到有明确结果才会返回,如果网络环境较差,可能要等一会,影响体验, 为了解决这个问题,我们使用异步co ...

  2. socket的阻塞模式和非阻塞模式(send和recv函数在阻塞和非阻塞模式下的表现)

    socket的阻塞模式和非阻塞模式 无论是Windows还是Linux,默认创建socket都是阻塞模式的 在Linux中,可以再创建socket是直接将它设置为非阻塞模式 int socket (i ...

  3. 非阻塞模式下 SEND 和 RECV 函数的返回值总结

    send 和 recv 函数的各种返回值意义: 返回值 n 返回值含义 大于 0 成功发送 n 个字节 0 对端关闭连接 小于 0( -1) 出错或者被信号中断或者对端 TCP 窗口太小数据发不出去( ...

  4. linux下网络编程设置非阻塞,UNIX网络编程 非阻塞connect的实现

    一.<UNIX网络编程>-非阻塞connect 在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未 ...

  5. socket接收时信号量阻塞了会丢数据吗_浅谈Java网络编程——非阻塞I/O

    文件描述符(descriptors) Unix中I/O的基本组成元素是字节序列.大多数程序应用于字节流或I/O流. 进程通过描述符引用I/O流,也被称作文件描述符.管道.文件.POSIX IPC's( ...

  6. python 网络编程----非阻塞或异步编程

    From: http://blog.chinaunix.net/uid-20730371-id-765038.html 非阻塞或异步编程 例如,对于一个聊天室来说,因为有多个连接需要同时被处理,所以很 ...

  7. linux网络编程-----非阻塞connect

    libevent在内部由于使用io多路复用函数进行的事件监控,所以它所有的io读写操作都是非阻塞的,换句话说,libevent提供的接口函数evconnlistener_new_bind()和buff ...

  8. Socket 非阻塞模式下connect 返回EINPROGRESS(115)错误

    今天再测试socket的时候,发现一个很奇怪的问题,就是客户端再connect的时候第一次connect总是会返回-1,errno是115,往往第二次连接就可以成功了.但是对于服务端来说,第一次连接已 ...

  9. 非阻塞模式WinSock编程入门

    介绍 WinSock是Windows提供的包含了一系列网络编程接口的套接字程序库.在这篇文章中,我们将介绍如何把它的非阻塞模式引入到应用程序中.文章中所讨论的通信均为面向连接的通信(TCP),为清晰起 ...

最新文章

  1. 百度李彦宏遭现场泼水,瞬间懵圈,湿身调侃AI遇到的挫折
  2. php读取cookie文件,PHP读取CURL模拟登录时生成Cookie文件的方法,_PHP教程
  3. 不同项目之间的控件共享
  4. (一)、http原理
  5. centos中使用rpm包或yum命令在线安装的软件默认是安装在那个目录下
  6. 我们公司也实行了OKR
  7. 用javascript写一个emoji表情插件
  8. linux查看history及操作时间,linux下查询history操作时间的方法
  9. 《菜菜的机器学习sklearn课堂》sklearn入门与决策树
  10. 启动Tomcat时报错,一大堆的
  11. WSDL文件生成Java代码
  12. Winform开发技术详解 - 应用环境 相关技术介绍
  13. Excel技能培训之六-定位功能,隔行插入删除空行,分组插入空行,高亮行列间差异,复制筛选后的数据
  14. chrome浏览器 在线打开预览pdf文件,而不是下载文件。
  15. java.util.sortedmap_Java SortedMap lastKey()用法及代码示例
  16. mov,lea指令的区别
  17. C/C++描述 LeetCode 周赛 第199场周赛(阿里云专场)
  18. 尝试用bert做文本聚类
  19. scratch扫雷游戏
  20. C/C++八大排序(c/c++)

热门文章

  1. Airflow使用入门指南
  2. 【原型设计】实用节:Axure RP9 的一些常用的快捷按键组合操作
  3. js原生实现过渡效果的返回顶部功能实例
  4. python发邮件实例_python 发送邮件实例
  5. Redis文档阅读笔记-Pub/Sub官方解析
  6. Linux笔记-压缩包安装jdk1.7(bash与.cshrc)
  7. struts2在Action中访问Session,管理员删除用户
  8. python中对字符串进行编码_Python 中的字符串编码
  9. python 爬虫1:发送请求
  10. mac 启动mysql 报错,mac 解决 mysql 启动报错