windows网络编程之Winsock(三)非阻塞select模式服务器

Windows C++ TCP编程实战:从Winsock基础到完整实现 TCP协议作为网络编程的核心基础,提供了面向连接、可靠传输的字节流服务,通过三次握手建立连接、序列号确认和超时重传等机制确保数据完整有序。在Windows平台下,这一技术通过Winsock API实现,与Linux的BSD套接字存在差异,需要专门的初始化和错误处理。掌握Winsock编程对于开发高性能网络应用如游戏服务器、工业控制软件等具有重要价值,能够帮助开发者构建稳定、高效的C/S架构系统。本文聚焦于Windows C++环境下的TCP连接实现,详细解析了Winsock初始化、套接字创建、数据收发等关键 阅读详情

// SelectModeWinSock.cpp : 定义控制台应用程序的入口点。
//
/************************************
 作者:  wangweixing2000
 Revision By: 0.01
 Revised on 2005-6-29 11:52:03
 Comments: SelectModeWinSock 该程序中存在bug,没有处理多线程数据访问的冲突和判断客户端断开的有效处理!哪位有兴趣可以加上!

************************************/
//select(选择)模型,是利用select函数实现对i/o的管理。select函数可以用于
//判断套接字上是否存在数据,或者能否向一个套接字写入数据。
/*
int select(
  int nfds,                       //被忽略参数,为了保持和早期Berkeley套接字应用程序兼容而保留的这个参数
  fd_set* readfds,                //检查可读性fd_set*参数
  fd_set* writefds,               //检查可写性fd_set*参数
  fd_set* exceptfds,              //带外数据
  const struct timeval* timeout   //指定select等待i/o操作完成时,最多等待多长时间。
);

typedef struct timeval
{
long tv_sec;     //以秒为单位指定等待的时间
long tv_usec;    //以毫秒为单位指定的等待时间
} timeval;
用select对套接字进行监听前,应用程序必须将套接字句柄分配给一个集合,设置好一个或所有的读、写以及例外的
fd_set结构。将一个套接字分配给任何一个集合后,再来调用select,便可知道某个套接字上是否正在发生i/o活动。

Winsock提供了几个宏用来处理fd_set:
FD_ZERO(*set)  //清空fd_set*
FD_CLR(s,*set)  //从set中删除s套接字
FD_ISSET(s,*set) //检查s是否是set集合的一名成员,如果是返回TRUE
FD_SET(s,*set)   //设置套接字s加入集合set中
*/
#include "stdafx.h"
#include <iostream>
#include <Winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
struct SocketObj
{
 SOCKET socket;   //当前对象的socket
 BOOL   listening; //该套接字是否已经
    SocketObj *next, //向后
        *prev;  //向前
};
SocketObj *g_pSocketList = NULL;  //Socket连表
SocketObj *g_pSocketEnd = NULL;   //连表的尾部
int        g_nSocketCount = 0;
HANDLE g_hSelect;

//创建SocketObj
SocketObj* GetSocketObj(SOCKET s,BOOL listening)
{
 SocketObj *newSocketObj = NULL;
 newSocketObj = (SocketObj*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SocketObj));
 if(newSocketObj == NULL)
 {
  cout<<"GetSocketObj: HeapAlloc failed: "<< GetLastError()<<endl;
        ExitProcess(-1);     //结束进程
 }
 newSocketObj->socket = s;
 newSocketObj->listening = listening;
 return newSocketObj;
}


//插入一个SocketObj
void InserSocketObj(SocketObj *obj)
{
 obj->next = obj->prev = NULL;
 if(g_pSocketList == NULL)
 {
  g_pSocketList = g_pSocketEnd = obj;
 }
 else
 {
  obj->prev = g_pSocketEnd;
  g_pSocketEnd->next = obj;
  g_pSocketEnd = obj;
 }
 g_nSocketCount++;
}
//删除
void RemoveSocketObj(SocketObj *obj)
{
 if(obj->prev)
 {
  obj->prev->next = obj->next;
 }
 if(obj->next)
 {
  obj->next->prev = obj->prev;
 }
 if(obj == g_pSocketList)
 {
  g_pSocketList = obj->next;
 }
 if(obj == g_pSocketEnd)
 {
  g_pSocketEnd = obj->prev;
 }
 g_nSocketCount--;
 HeapFree(GetProcessHeap(),0,obj);
}
//监听线程
DWORD WINAPI  ListenThread(void *pVoid)
{
 cout<<"server start listening!"<<endl;

 SOCKADDR_IN ClientAddr;                   // 定义一个客户端得地址结构作为参数
 int addr_length=sizeof(ClientAddr);
 SOCKET *listen = (SOCKET*)pVoid;
 SocketObj *pSocketObj = NULL;
 while(1)
 {
  SOCKET Client = accept(*listen,(sockaddr*)&ClientAddr,&addr_length);
  if(Client == INVALID_SOCKET)
  {
   cout<<"accept failed!"<<endl;
   continue;
  }
  // 这里可以取得客户端的IP和端口,但是我们只取其中的SOCKET编号
  LPCTSTR lpIP =  inet_ntoa(ClientAddr.sin_addr);
  UINT nPort = ClientAddr.sin_port;
  cout<<"一个客户端已经连接!IP:"<<lpIP<<"SOCKET 端口号:"<<nPort<<endl;
  //创建SocketObj并添加如list
  pSocketObj = GetSocketObj(Client,TRUE);
  InserSocketObj(pSocketObj);
  if(g_nSocketCount == 1)   //如果有一个客户端就唤起select线程
  {
   ResumeThread((HANDLE)g_hSelect);
  }
 }
 return 0;
}
//select线程函数
DWORD WINAPI  SelectThread(void *pVoid)
{
 SocketObj *sptr = NULL;
 fd_set readfds,
     writefds,
     exceptfds;
 timeval timeout;
 char    Buffer[4096];
 while(TRUE)
 {
  //清空fd_set
  FD_ZERO(&readfds);
  FD_ZERO(&writefds);
  FD_ZERO(&exceptfds);

  //设置timeout
  timeout.tv_sec = 5;
  timeout.tv_usec = 0;

  sptr = g_pSocketList;
  //设置fd_set
  while(sptr)        
  {
   FD_SET(sptr->socket,&readfds);
   FD_SET(sptr->socket,&writefds);
   FD_SET(sptr->socket,&exceptfds);
   sptr = sptr->next;
  }

  //开始select
  int ret = select(0,&readfds,&writefds,NULL,&timeout);
  if(ret == SOCKET_ERROR)
  {
   cout<<"select failed!"<<endl;
   WSACleanup();
   return -1;
  }
  else if(ret == 0)
  {
   //超时
   cout<<"time out!"<<endl;
   continue;
  }
  else
  {
   sptr = g_pSocketList;
   while(sptr)
   {
    if(FD_ISSET(sptr->socket,&readfds))
    {
     ZeroMemory(Buffer,4096);
     int re = recv(sptr->socket,Buffer,4096,0);
     if(re == SOCKET_ERROR)
     {
      return -1;
     }
     else if(re == 0)
     {
      //该客户端已经断开
      closesocket(sptr->socket);
      SocketObj *tmp = sptr;
      sptr = sptr->next;
      RemoveSocketObj(tmp);
      continue;
     }
     else
     {
      cout<<"recv buffer:"<<Buffer<<endl;
     }
    }
    if(FD_ISSET(sptr->socket,&writefds))
    {
     //发送数据给当前客户端
     char *sendBuffer = "wwx send msg!";
     int re = send(sptr->socket,sendBuffer,16,0);
     if(re == SOCKET_ERROR)
     {
      return -1;
     }
     else if(re == 0)
     {
      continue;
     }
     else
     {
      //这里可以显示发送是否成功
      //cout<<"send successed!"<<endl;
     }
    }
    sptr = sptr->next;
   }
  }
 }

 return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
 WSAData wsaData;
 SOCKET  Listen;
 SocketObj *pSockobj = NULL;
 SOCKET  Accept = INVALID_SOCKET;
 SOCKADDR_IN ServerAddr;
 

 //初始化Winsock库
 int ret = WSAStartup(MAKEWORD(2,2),&wsaData);
 if(ret != 0)
 {
  cout<<"WSAStartup error!"<<endl;
  WSACleanup();
  return -1;
 }
 
 Listen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 if(Listen == INVALID_SOCKET)
 {
  cout<<"Listen create failed!"<<endl;
  WSACleanup();
  return -1;
 }

 //绑定
 ServerAddr.sin_family = AF_INET;
 ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
 ServerAddr.sin_port = htons(10012);
 ret = bind(Listen,(sockaddr*)&ServerAddr,sizeof(SOCKADDR_IN));
 if(ret == SOCKET_ERROR)
 {
  cout<<"bind failed!"<<endl;
  closesocket(Listen);
  WSACleanup();
  return -1;
 }

//监听
 listen(Listen,200);
 
 //开启监听线程
 HANDLE hListen = CreateThread(NULL,0,ListenThread,&Listen,0,NULL);
 if(hListen == NULL)
 {
  cout<<"Create ListenThread failed!"<<endl;
 }
 //创价挂起的select线程,因为刚开始没有连接的客户端
 g_hSelect = CreateThread(NULL,0,SelectThread,NULL,CREATE_SUSPENDED,NULL);
 if(g_hSelect == NULL)
 {
  cout<<"Create SelectThread failed!"<<endl;
 }
 system("PAUSE");
 return 0;
}

如果引用该文,请注明出处!谢谢!

Windows C++ TCP通信实战:从Winsock API到粘包处理与编译避坑 TCP(传输控制协议)是网络编程中实现可靠数据传输的核心基石,它通过面向连接、数据确认和重传机制确保字节流的准确送达。Socket(套接字)作为其编程接口,为进程间通信提供了端点抽象。在Windows平台,Winsock API是实现Socket编程的标准库,其价值在于为构建高性能、稳定的网络应用(如游戏服务器、即时通讯系统)提供了底层支持。掌握Winsock的初始化、地址绑定、监听与连接流程,是进行C++网络开发的必备技能。实践中,开发者常需处理TCP粘包/拆包问题,并需注意在Visual Studio与 阅读详情

相关推荐

让 AI 变聪明的 20 条指令:如何让大模型回答得更精准、更专业、更实事求是

让 AI 变聪明,本质上不是写几句神奇咒语,而是把你的任务表达得更清楚,把事实标准设得更严格,把输出格式规定得更明确,把不确定性管理得更认真,把历史对话利用得更充分。大模型的强项是语言生成、信息组织、模式归纳和方案扩展;弱项是事实核验、实时更新、责任判断和对模糊意图的准确猜测。好的指令,就是在用人的判断弥补模型的弱点,让模型把强项发挥在正确的地方。

Sumarua的博客 1508

windows环境下设置socket为非阻塞模式

socket 阻塞模式

xkdlzy的专栏 1079

【软考高项】五十一、常见必会的几种进度网络图(单代号、双代号、七格图、横道图)

软考高项计算题进度网络图画图介绍

有莘不破的博客 3万+

Winsock 套接字非阻塞模式小例程 WinSock IO模型

服务器端使用非阻塞套接字,允许有多个客户接入。客户端使用阻塞套接字。服务器端循环轮询方式。 /***设套接字为非阻塞模式***/ unsigned long ul = 1; int nRet=ioctlsocket(sock_server, FIONBIO, &amp;ul); //设置套接字非阻塞模式 if (nRet == SOCKET_ERROR) { cout &lt;&lt...

m0_37357063的博客 3102

完美解决unknown failed to transfer from http://0.0.0.0/ during a previous attempt的错误

完美解决unknown failed to transfer from http://0.0.0.0/ during a previous attempt的错误

念兮为美 2281

智能绿植管家:基于 ESP32 的室内植物自动补光与环境调控系统

本文设计的基于 ESP32 的室内植物自动补光与生长环境调控系统,实现了植物生长环境的自动化、智能化管理。硬件选型合理、接线简单,软件逻辑清晰、易于调试,能够有效解决室内植物生长环境不适的问题。系统具备良好的实用性和扩展性,可为园艺爱好者和小型培育场所提供稳定、高效的植物养护方案。

weixin_42300449的博客 147

Socket编程系列之2:Windows-API网络编程入门实战

Socket编程系列初步计划:Socket编程系列之1: Linux-API 网络编程入门实战Socket编程系列之2: Windows-API 网络编程入门实战Socket编程系列之3: Muduo 网络编程入门实战Socket编程系列之4: Libevent 网络编程入门实战Socket编程系列之5: Libcurl 网络编程入门实战Socket编程系列之6: Boost-ASIO 网络编程入门实战----------------------------------------------------Socket编程系列之2:Windows-API网络编程入门实战l Winsock编程接口l UDP程序设计l TCP套接口函数l Web客户/服务器程序l 基于windows的IO复用机制l UDP广播和多播l 套接字选项l Windows套接字I/O模型l 原始套接字与ICMPl WinInet简介与案例实战 

第1章第11节:创建可容纳多行滚动文字的输入框TextEditor [SwiftUI快速入门到实战]

TextField可以让用户输入单行的文字内容,若想输入多行的文字内容,可以使用能够容纳多行滚动文字的 TextEditor。 定义一个字符串属性,用来存储用户输入的文字内容。接着定义一个布尔属性,用来标识是否弹出警告窗口,以显示用户输入的内容。 TextEditor是由UIKit中的UITextView演变而来,因此可以在init方法中,通过UITextView修改TextEditor的外观属性。 添加一个VStack视图,作为子视图的容器。 创建一个TextEditor,Te..

互动教程网[www.hdjc8.com] 832

网络编程6: 如何将一个socket设置为非阻塞(windows和Linux)

如何将一个socket设置为非阻塞(windows和Linux)

yubaoaihh 2186

Debian Buster 软件源失效问题

摘要:基于Debian Buster的ARM64设备因系统版本结束支持(EOL)导致apt update报错404。解决方案包括:1)改用Debian官方存档源(需禁用安全检查);2)升级至受支持的Debian 11/12版本(推荐);3)临时使用第三方镜像源(不推荐)。操作步骤涉及修改sources.list、处理NTP同步问题及清理残留配置。建议优先采用版本升级方案以获得持续安全更新,避免使用过时系统带来的安全隐患。文中提供了完整命令示例和操作记录供参考。

yy_xzz的博客 2179

Easy系列PLC远程IO模块的安装配置和地址访问(EtherCAT总线)

EtherCAT分布式时钟DC(SOEM源码解析)EtherCAT分布式时钟DC(SOEM源码解析)_soem dc同步-优快云博客文章浏览阅读1.2k次。分布式时钟(DC,Dsitributed Clock)可以使所有 EtherCAT设备使用相同的系统时间,从而控制各设备任务的同步执行。_soem dc同步1200PLC连接分布式IO组态编程应用(ES200SP固件版本不同解决方法)

RXXW_Dor的博客 176

Winsock网络编程笔记:select()函数详解,select例子实现非阻塞TCPServer

套接字集合: fd_set (defined in winsock2.h) fd_set结构可以把多个套接字集合在一起,形成一个套接字集合。select函数可以测试这个集合中哪些套接字有事件发生。 typedef struct fd_set { u_int fd_count; // how many are SET ...

Romantic Chopin in C Sharp Minor 2200

Q3D提取PCB走线RLCG参数

介绍一种提取PCB走线寄生电容和寄生电感的方法

sinat_15150363的博客 2923

windows网络编程之 (三) - Winsock非阻塞select模式服务器

/************************************ 作者:  wangweixing2000 Revision By: 0.01 Revised on 2005-6-29 11:52:03 Comments: SelectModeWinSock 该程序中存在bug,没有处理多线程数据访问的冲突和判断客户端断开的有效处理!哪位有兴趣可以加上!***************

Civet148的专栏 7720

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

WinSock使用Select设置connect的超时

设置connect的超时很简单,优快云上也有人提到过使用select,但却没有一个令人满意与完整的答案。偶所讲的也正是select函数,此函数集成在winsock1.1中,简单点讲, "作用使那些想避免在套接字调用过程中被锁定的应用程序,采取一种有序的方式,同时对多个套接字进行管理 "(《Windows网络编程技术》原话)。使用方法与解释请见《Windows网络编程技术》。   在使用此函数前

五环之外 3784

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Winsock网络编程套接字I/O模型之选择模型

Winsock 提供了一些 I/O 模型帮助应用程序以异步方式在一个或者多个套接字上管理 I/O。

weixin_43446169的博客 1316

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

winsock IO模型 select模型

两种I/O模式 * 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序。套接字 默认为阻塞模式。可以通过多线程技术进行处理。 * 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权。这种模式使用 起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回 WSAEWOULDBLOCK错误。但功能强大。 比较容易想到的一种服务器模型就是采用一个主线程,负责监听客户...

逐步前行 199

windows网络编程之Winsock非阻塞select模式服务器

//select(选择)模型,是利用select函数实现对i/o的管理。select函数可以用于 //判断套接字上是否存在数据,或者能否向一个套接字写入数据。 /* int select( int nfds,                       //被忽略参数,为了保持和早期Berkeley套接字应用程序兼容而保留的这个参数 fd_set* readfds,           

zqf_office的专栏 566

基于winsock的阻塞和非阻塞通信模型

copy:[url]http://hi.baidu.com/yaofly/blog/item/a0668b4bf71449f882025c50.html[/url] 这文章貌似不错,sorry,习惯用貌似了~ 摘要:在应用程序开发中,经常涉及各式各样的机器的交互通信问题。在Windows操作系统下,可以使用MFC中的CSocket,也可以使用以Windows Api为基础的Winsoc...

cm00337533的专栏 410

非阻塞connect用法

在广域网中,connect函数可能需要比较长的时间返回(等待对端发送ack),所以我们通常需要非阻塞connect。下面分别实现windows和linux的非阻塞connect,并作出对比。 先大概总结一下winsock和linuxsocket用法区别: winsock需要WSAStartup和WSACleanupleanup进行初始化和反初始化,linuxsocket不用 关闭套接字,...

回头太难 5612

Windows下C++ Select模型Socket编程:从原理到Echo服务器实战

同步I/O复用是网络编程中的核心概念,它允许单个线程高效管理多个网络连接,通过监视套接字状态来避免为每个连接创建独立线程的开销。其工作原理基于操作系统内核提供的就绪通知机制,应用程序可以批量检查多个套接字的可读、可写或异常状态。这一技术价值在于显著提升资源利用率,降低上下文切换成本,是实现高并发服务器的基础模型,广泛应用于聊天服务器、游戏网关和轻量级代理等场景。在Windows平台,Select模型作为最基础的多路复用实现,其与Linux/Unix的差异、FD_SETSIZE限制以及非阻塞套接字处理是工程实

weixin_34152820的博客 345

Windows Sockets网络编程:从阻塞模型到IOCP高性能服务器实战

网络编程是构建分布式系统和网络应用的基础,其核心在于理解操作系统提供的套接字(Socket)接口。在Windows平台,Windows Sockets(Winsock)是进行TCP/IP网络通信的标准API,它基于伯克利套接字标准,并针对Windows系统进行了扩展和优化。掌握其原理,尤其是从基础的阻塞I/O到异步I/O模型的演进,对于开发高性能、高并发的网络服务至关重要。完成端口(IOCP)作为Windows下最高效的I/O模型,通过异步操作和少量工作线程管理海量连接,能极大提升服务器的吞吐量和可伸缩性,

weixin_33690963的博客 408

Windows学习笔记-25(网络编程)

网络编程是指编写应用程序,使其能够通过网络与其他计算机上的进程进行通信。在 Windows 平台上,最核心的网络编程接口是,它基于 Berkeley Sockets 规范,并扩展了 Windows 特有的异步模型。:面向连接、可靠、基于字节流的协议。适用于需要可靠数据传输的场景,如 HTTP、FTP、邮件等。:无连接、不可靠、基于数据报的协议。适用于实时性要求高、可容忍丢包的场景,如视频通话、在线游戏、DNS 查询等。

2301_79306123的博客 713

WinSock I/O编程模型之——Select模型

最近,在系统地学习windows网络编程。虽然,已经浏览过select相关代码,查阅过很多书籍和资料,但是自始自终都是一副朦胧的姿态,一知半解,不知所以。为了更好的学习网络编程,决定将多线程和套接字I/O模型这块知识进行彻底的总结一下,书写一些测试代码,将这块堡垒攻下。当然自己不是什么大师,这方面知识相对匮乏,或多获少会存在一些歧义,希望能在不断的学习过程中,总结不足,及时补充。

tzwh_86的专栏 721
上一篇: 线程核心对象的引用计数
下一篇: AFX中的全局函数列表
wangweixing2000
博客等级 码龄24年 554粉丝 94原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值