简述

这里使用的API和同步编程的API是差不多的,只多了一个ioctlsocket和select函数。这里面涉及一个很重要的结构体fd_set。这里用到的API大部分都是windows和linux通用的。

1. ioctlsocket控制socket的IO模型

int ioctlsocket(_In_    SOCKET s,_In_    long   cmd,_Inout_ u_long *argp
);

s:需要设置的socket

cmd:想要对socket执行的命令,异步编程需要FIONBIO命令

argp:执行命令的参数,FIONBIO的参数如果为0表示阻塞模式,非0表示非阻塞模式

2. select获取一个或多个套接字的状态(可读可写或其他状态)

int select(_In_    int                  nfds,_Inout_ fd_set               *readfds,_Inout_ fd_set               *writefds,_Inout_ fd_set               *exceptfds,_In_    const struct timeval *timeout
);

nfds:无用

readfds:一个fd_set,表示哪些套接字需要判断是否”可读”状态,其中”可读”状态可以是accept,recv或者套接字已经关闭,重置,中断。

writefds:一个fd_set,表示哪些套接字需要判断是否”可写”状态,其中”可写”状态可以是connect成功,send。一般来说send是会立刻返回的,但是当send缓存区被装满了,数据无法放入时就会导致send函数阻塞,异步模式下可以用select判断缓存区是否有空间。

exceptfds:一个fd_set,表示哪些套接字需要判断是否”异常”状态,其中”异常”状态一般是connect失败。

timeout:等待的超时时间,select会等待timeout毫秒,NULL表示无限等待

成功返回所有fd_set响应的套接字数目,错误发生返回SOCKET_ERROR,超时返回0.

3. fd_set就是一个简单的结构体,内部有一个socket数组和一个数组成员个数。

typedef struct fd_set {u_int fd_count;               /* how many are SET? */SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

可以使用如下宏操作fd_set

FD_CLR(fd, set):从fd_set中删除指定的socket

FD_SET(fd, set):从fd_set中添加指定的socket

FD_ZERO(set):清空fd_set

FD_ISSET(fd, set):判断指定socket是否在fd_set中

异步通信示例

下面是异步通信服务端的代码,用于回发客户端发过来的消息,单线程中处理多个客户端的通信。

#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib")#define IP "127.0.0.1"
#define DEFAULT_PORT 12345#define Print_ErrCode(e) fprintf(stderr,"\n[Server]%s 执行失败: %d\n",e,WSAGetLastError())
#define DEFAULT_BACKLOG 5
#define MAX_IO_PEND 10
int curr_size = 0; //当前的句柄数
#define OP_READ 0x10
#define OP_WRITE 0x20//定义结构体用于储存通信信息
typedef struct _socklist
{SOCKET sock;DWORD Op;char name[100];char Buffer[128];int  bufLen;
} Socklist;int main(int argc, char **argv)
{int nStartup = 0;struct sockaddr_in clientService;WSADATA wsaData;SOCKET sockListen = INVALID_SOCKET;int nRet = 0;//保存所有的客户端、服务端的SOCKET信息if (0 != (nStartup = WSAStartup(MAKEWORD(2, 2), &wsaData))){WSASetLastError(nStartup); //WSAStartup不会自动设置错误代码Print_ErrCode("WSAStartup()");return 1;}clientService.sin_family = AF_INET;clientService.sin_addr.s_addr = inet_addr(IP);clientService.sin_port = htons(DEFAULT_PORT);if (INVALID_SOCKET == (sockListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))){Print_ErrCode("socket()");return 1;}u_long type = 1;ioctlsocket(sockListen, FIONBIO, &type);if (SOCKET_ERROR == bind(sockListen,(SOCKADDR *)&clientService,sizeof(clientService))){Print_ErrCode("bind()");closesocket(sockListen);return 1;}if (SOCKET_ERROR == listen(sockListen, DEFAULT_BACKLOG)){Print_ErrCode("listen()");closesocket(sockListen);}printf("[Server]监听 %s:%d\n", IP, DEFAULT_PORT);//存放所有的socket,包括用于accept的socket。Socklist sockList[10];//将监听socket设为socklist第一个元素sockList[0].sock = sockListen;curr_size = 1;// 一个大循环,不断的接收客户端请求while(true){//循环判断是否有请求需要处理
        fd_set fdRead, fdWrite;while (true){FD_ZERO(&fdRead);FD_ZERO(&fdWrite);FD_SET(sockList[0].sock, &fdRead);for (int i = 1; i < curr_size; i++){//对需要send的客户端连接selectif (sockList[i].Op == OP_WRITE){FD_SET(sockList[i].sock, &fdWrite);}//对所有的客户端连接selectFD_SET(sockList[i].sock, &fdRead);}//这个操作会被阻塞nRet = select(0, &fdRead, &fdWrite, NULL, NULL);if (FD_ISSET(sockList[0].sock, &fdRead)){//第0个socket可用了,这时accept一定会立刻返回成功或失败 这里需要处理最大连接数SOCKET sockNewClient = accept(sockListen,NULL,NULL);sockList[curr_size].sock = sockNewClient;sockList[curr_size++].Op = OP_READ;break;}//其他socket可用了,判断哪些能读,哪些能写if (fdRead.fd_count > 0){for (int i = 1; i < curr_size; i++){if (FD_ISSET(sockList[i].sock, &fdRead)){//开始recvnRet = recv(sockList[i].sock, sockList[i].Buffer, 127, 0);if (nRet == SOCKET_ERROR){closesocket(sockList[i].sock);//移除sockListfor (int j = i; j < curr_size-1; j++){sockList[i].sock = sockList[i + 1].sock;}curr_size--;}else{sockList[i].Buffer[nRet] = '\0';sockList[i].bufLen = nRet;sockList[i].Op = OP_WRITE;printf("[Server]接收到:%s\n", sockList[i].Buffer);}}}}if (fdWrite.fd_count > 0){for (int i = 1; i < curr_size; i++){if (FD_ISSET(sockList[i].sock, &fdWrite)){if (sockList[i].Op == OP_WRITE){//开始sendnRet = send(sockList[i].sock, sockList[i].Buffer, sockList[i].bufLen, 0);//事实上,这里可能会有nRet小于bufLen的情况if (nRet == SOCKET_ERROR){closesocket(sockList[i].sock);//移除sockListfor (int j = i; j < curr_size - 1; j++){sockList[i].sock = sockList[i + 1].sock;}curr_size--;}else{sockList[i].Op = OP_READ;printf("[Server]已发送:%d\n", nRet);}}}}}}}
}

转载于:https://www.cnblogs.com/Reyzal/p/6759978.html

Windows网络通信(二):socket异步编程相关推荐

  1. Django3(二)异步编程

    目录 一.使用多线程 二.启用ASGI服务 三.异步视图 异步与同步的转换 其他:消息队列(基本概念) 异步编程:使用协程.线程.进程.消息队列等方式实现. Django支持多线程.内置异步和消息队列 ...

  2. Windows/Linux TCP Socket网络编程简介及测试代码

    典型的网络应用是由一对程序(即客户程序和服务器程序)组成的,它们位于两个不同的端系统中.当运行这两个程序时,创建了一个客户进程和一个服务器进程,同时它们通过从套接字(socket)读出和写入数据在彼此 ...

  3. Linux与Windows下的socket网络编程对比

    阅读前请看一下:我是一个热衷于记录的人,每次写博客会反复研读,尽量不断提升博客质量.文章设置为仅粉丝可见,是因为写博客确实花了不少精力.希望互相进步谢谢!! 文章目录 阅读前请看一下:我是一个热衷于记 ...

  4. Windows下的socket编程

    前言 经过一周的时间,我又回来啦,这周我主要学习的是Windows下的socket网络编程.本篇博客的内容包括socket的简介.TCP/IP协议的讲解.TCP socket编程实例.UDP sock ...

  5. 认识Java异步编程

    一 .认识异步编程 通常Java开发人员喜欢使用同步代码编写程序,因为这种请求(request)/响应(response)的方式比较简单,并且比较符合编程人员的思维习惯;这种做法很好,直到系统出现性能 ...

  6. 一文说通C#中的异步编程

    天天写,不一定就明白. 又及,前两天看了一个关于同步方法中调用异步方法的文章,里面有些概念不太正确,所以整理了这个文章.   一.同步和异步. 先说同步. 同步概念大家都很熟悉.在异步概念出来之前,我 ...

  7. c++ 异步下获取线程执行结果_前端异步编程的那些事

    啊 一.异步编程的运行机制 我们学习Javascript语言的时候就知道它的执行环境是"单线程"的. 所谓"单线程",就是指一次只能处理一个任务.如果有多个任务 ...

  8. 异步编程的方式有哪几种?

    目录 前言 一.JS任务的执行模式 二.异步编程的六种方式 1.回调函数方式 2.Promise对象 3.事件监听 4.发布/订阅 5.async/await 函数的实现 6.生成器函数 Genera ...

  9. python3异步编程_协程 Python异步编程(asyncio)

    协程(Coroutine) 也可以被称为微线程,是一种用户态内的上下文切换技术.简而言之,其实就是通过一个线程实现代码块相互切换执行. 直接上代码,例如: 同步编程 import time def f ...

最新文章

  1. Log4j官方文档翻译(九、输出到数据库)
  2. ClickHouse系列教程四:允许远程连接 allow remote access
  3. tensorflow 进阶(三),BP神经网络之两层hidden_layer
  4. window把图片转成blob_将图片转成Base64,再将Base64转成Blob
  5. JAXB 有两个名为 ** 的属性,类的两个属性具有相同名称 **解决方案
  6. [Alg] 二叉树的非递归遍历
  7. matlab 当已知两个矩阵满足分别一定条件时_程序继续执行,MATLAB程序设计基础
  8. 前端判断是否为空字符窜
  9. Java NIO学习篇之NIO的基本认识
  10. 使用LINQ计算基本统计
  11. qpushbutton里面的文字怎么换行_ipad读PDF必备,OCR局部识别文字并快速提取,免费的buff你要不要?...
  12. python用tesseract-ocr做图像识别
  13. cordova环境配置步骤
  14. 对整个网页进行长截图
  15. Node学习 | Day04 数据库与身份验证(数据库的基本概念、安装并配置MySQL、MySQL的基本使用、在项目中操作MySQL、前后端的身份验证)
  16. hardfault常见原因_STM32如何查找hardfault原因
  17. 联发科毫米波雷达解决方案芯片MT2706(Autus R10)
  18. 海洋cms宝塔定时linux,海洋cms怎么设置宝塔自动采集教程
  19. Java的来源和基础语法
  20. linux无线图传,uClinuxforBV561EVB-3G4G无线图传4G执法记录.PDF

热门文章

  1. win7右键新建文件夹不见了
  2. 回首2013 展望2014
  3. 广东时代互联---网络管理面试
  4. Android TextView 初步学习
  5. 【总结整理】如何做需求分析(转)
  6. Swift进阶学习笔记
  7. 分享mac磁盘清理的方法
  8. Libgdx New 3D API 教程之 -- 使用Libgdx加载模型
  9. IBM db2安装好了以后,启动不了服务
  10. c++返回值 注意事项