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

原创 2006年06月01日 00:41:00
/************************************
 作者:  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;
}

『转载』 作者Blog:http://blog.csdn.net/wangweixing2000/

相关文章推荐

windows Socket编程之select网络模型

在此之前呢,介绍了TCP/UDP的服务端的实现。但是,它们有很大的缺点,比如说,效率很低,开销太大等。因此,接下来我们先介绍select网络模型。 我们在TCP的服务端里边,接收一个客户端的时候...
  • Timmiy
  • Timmiy
  • 2016年08月08日 23:08
  • 1836

windows socket编程:select 用法 例子

http://blog.csdn.net/zjsiva/article/details/5895087 #include #include #include #pragm...

winsock中select的作用

 select函数用来填充一组可用的socket句柄,当满足如下条件时:1.可以读取的sockets。当这些socket被返回时,在这些socket上执行recv/accept等操作不会产生阻塞;2....
  • dadalan
  • dadalan
  • 2008年10月15日 13:03
  • 847

winsock Select模型

Windows在非阻塞和阻塞模式下执行I/O操作。在阻塞模式下TCP对一个IP的端口只能执行完一个客户的请求才能处理下一个请求,各个请求之间排队按顺序执行,这就是同步。异步就是同时来两个或者多个请求,...
  • yhcfly
  • yhcfly
  • 2013年01月21日 10:38
  • 412

winsock中select模型实战

选择(select)模型是Winsock中最常见的 I/O模型。核心便是利用 select 函数,实现对 I/O的管理!利用 select 函数来判断某Socket上是否有数据可读,或者能否向一个套接...

Windows网络编程之Select模型学习笔记

关于select模型的理论讲解,网上随便一搜就有很多大神的精彩文章,这里就不重复造轮子了。不过要真正理解select模型,代码才是最好的文章。我在网上看了好多代码,可能是相互转载的原因,有些代码不是编...

Windows网络编程之(二)Socket通信非阻塞模式Select(TCP和UDP)

阻塞式的Socket很容易理解,但是使用起来非常别扭。Windows提供了选择(Select)I/O模型,进行异步Socket通信. Select模型 int select( _In_ ...

基于select模型的TCP服务器

之前的一篇博文是基于TCP的服务器和客户机程序,今天在这我要实现一个基于select模型的TCP服务器(仅实现了服务器)。 socket套接字编程提供了很多模型来使服务器高效的接受客户端的请求,se...

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

基于winsock的阻塞和非阻塞通信模型 李 峰 (电信科学技术第十研究所 陕西 西安   710061) 摘要:在应用程序开发中,经常涉及各式各样的机器的交互通信问题。在W...

WinSock2编程之打造完整的SOCKET池

在Winodows平台上,网络编程的主要接口就是WinSock,目前大多数的Windows平台上的WinSock平台已经升级到2.0版,简称为WinSock2。在WinSock2中扩展了很多很有用的W...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:windows网络编程之 (三) - Winsock非阻塞select模式服务器
举报原因:
原因补充:

(最多只允许输入30个字)