大胖的部落格

Just a note

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  112 随笔 :: 0 文章 :: 3 评论 :: 0 Trackbacks
客户端向服务器发送数据,并显示服务器反馈的内容。
服务器采用IOCP模型,把每个客户端发送的数据向所有连接的客户端发送一遍。
运行一个服务器,可以连接多个运行的客户端。


服务器:
#include <iostream>
#include 
<winsock2.h>
#pragma comment (lib, 
"Ws2_32.lib")

using namespace std;


char            SERVER_IP[32]    = "127.0.0.1";
const DWORD        SERVER_PORT         = 1555;
const DWORD        BUFFER_SIZE         = 1024;

//OverLapped结构体扩展
struct OVERLAPPEDPLUS
{
    OVERLAPPED ol;
    
char    buf[BUFFER_SIZE];    //在SOCKET上收数据的buffer
    char    clientIP[32];        //客户端IP
    u_short    clientPort;       //客户端port
    DWORD    index;                 //SOCKET在数组中的index,从0开始
};

SOCKET    sock_array[
64]    = {NULL};    //保存客户端SOCKET
DWORD    sock_num        = 0;        //记录SOCKET数


//线程函数声明
DWORD CALLBACK ServerWorkerThread(void* CompletionPortID) ;

//Start function
void StartServer()
{
    WSADATA wd;
    ::WSAStartup(MAKEWORD(
2,2), &wd);

    sockaddr_in addr;
    addr.sin_family            
= AF_INET;
    addr.sin_port            
= htons(SERVER_PORT);
    addr.sin_addr.s_addr    
= inet_addr(SERVER_IP);

    SOCKET s 
= ::socket(AF_INET, SOCK_STREAM, 0);
    
if(0 != ::bind(s, (const sockaddr*)&addr, sizeof(addr)))
    {
        cout
<<"bind error: "<<WSAGetLastError()<<endl;
        ::closesocket(s);
        ::WSACleanup();
        
return;
    }
    ::listen(s, 
2);

    
//创建完成端口
    HANDLE CompetionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

    
//获得CPU数,创建CPU数*2+2个线程
    SYSTEM_INFO SystenInfo;
    ::GetSystemInfo(
&SystenInfo);
    
for(DWORD i=0; i<SystenInfo.dwNumberOfProcessors*2+2++i)
    {
        DWORD ThreadID 
= 0;
        HANDLE ThreadHandle 
= ::CreateThread(NULL,0,ServerWorkerThread,CompetionPort,0,&ThreadID);
        ::CloseHandle(ThreadHandle);
    }

    cout
<<"Server Started!"<<endl<<endl;
    
while(TRUE)
    {
        sockaddr_in client_addr;
        
int fromlen = sizeof(sockaddr_in);
        SOCKET ns 
= ::accept(s, (sockaddr*)&client_addr, NULL);

        
//SOCKET入数组
        sock_array[sock_num] = ns;
        
++sock_num;

        
//新客户端连接进来
        ::CreateIoCompletionPort((HANDLE)ns,CompetionPort,(DWORD)ns,0);
        
        
//在堆中创建OverLapped扩展结构体的对象
        OVERLAPPEDPLUS* olp = (OVERLAPPEDPLUS*)::GlobalAlloc(GPTR, sizeof(OVERLAPPEDPLUS));

        
//根据SOCKET取客户端的IP和PORT,保存在OVERLAPPEDPLUS里
        ZeroMemory(&client_addr, sizeof(sockaddr_in));
        ::getpeername(ns, (sockaddr
*)&client_addr, &fromlen);
        strcpy_s(olp
->clientIP, 32, inet_ntoa(client_addr.sin_addr));
        olp
->clientPort = ntohs(client_addr.sin_port);
        cout
<<endl<<olp->clientIP<<":"<<olp->clientPort<<" Connected!"<<endl<<endl;

        
//保存SOCKET的index
        olp->index = sock_num-1;

        WSABUF    wBuffer;
        wBuffer.buf 
= olp->buf;
        wBuffer.len 
= BUFFER_SIZE;
        DWORD    dwFlag 
= 0;        
        DWORD     dwRecvd 
= 0;
    
        
//在指定SOCKET上投递一个异步的IO请求
        ::WSARecvFrom(ns, &wBuffer, 1&dwRecvd, &dwFlag, (sockaddr*)&client_addr, &fromlen, &(olp->ol), NULL);
    }
    ::closesocket(s);
    ::WSACleanup();
}

//工作者线程
DWORD CALLBACK ServerWorkerThread(void* CompletionPortID)
{
    HANDLE CompletionPort 
= (HANDLE) CompletionPortID; 
    DWORD BytesTransferred 
= 0
    SOCKET mySock; 
    OVERLAPPEDPLUS
* polp; 
    DWORD dwSend 
= 0, dwRecv = 0, dwFlag = 0;

    
while(TRUE)
    {
        
//从指定的CP队列中取出一个完成包,一个完成包表示一次IO操作的完成;若队列为空则阻塞
        ::GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&mySock, (OVERLAPPED**)&polp, INFINITE);
        
        
if (BytesTransferred == 0
        {
            
//如果客户端关闭连接,则服务器端关闭SOCKET,释放申请的堆内存空间
            --sock_num;
            ::closesocket(mySock);
            DWORD dwIndex 
= polp->index;
            ::GlobalFree(polp);
            cout
<<endl<<polp->clientIP<<":"<<polp->clientPort<<" Disconnected!"<<endl<<endl;

            
//删掉的SOCKET后面往前挪
            for(DWORD i=dwIndex; i<sock_num; ++i)
            {
                sock_array[i] 
= sock_array[i+1];
            }
            
continue;    //不要关闭工作者线程
        }
        
        WSABUF    wBuffer;
        wBuffer.buf 
= polp->buf;
        wBuffer.len 
= BUFFER_SIZE;      

        
//将收到的数据反馈给每个客户端
        for(DWORD i=0; i<sock_num; ++i)
        {
            ::send(sock_array[i], polp
->buf, (int)strlen(polp->buf)+10);
        }
    
        
//输出收到的数据,并投递一个WSARecv请求
        cout<<polp->clientIP<<""<<polp->buf<<endl;
        ::WSARecv(mySock, 
&wBuffer, 1&dwSend, &dwFlag, &(polp->ol), NULL);
    }

    
return 0;
}

int main()
{
    cout
<<"Input your IP:"<<endl;
    cin
>>SERVER_IP;
    StartServer();
    
return 0;
}

客户端:
#include <iostream>
#include 
<winsock2.h>
#pragma comment (lib, 
"Ws2_32.lib")

using namespace std;

char SERVER_IP[32]    = "127.0.0.1";
const DWORD    SERVER_PORT    = 1555;

//线程函数声明
DWORD CALLBACK ReceiveThread(void* CompletionPortID) ;

int main()
{
    cout
<<"Input server IP:"<<endl;
    cin
>>SERVER_IP;

    
//初始化使用socket函数要用到的dll
    WSADATA wd;
    WSAStartup(MAKEWORD(
2,2), &wd);

    
//服务器地址信息
    sockaddr_in addr;
    addr.sin_family            
= AF_INET;
    addr.sin_port              
= htons(SERVER_PORT);
    addr.sin_addr.s_addr       
= inet_addr(SERVER_IP);

    
//创建套接字,并与服务器连接
    SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0);

    cout
<<"Connecting to: "<<SERVER_IP<<":"<<SERVER_PORT<<endl;
    
while(0 != ::connect(s, (const sockaddr*)&addr, sizeof(addr)))
    {

    }
    cout
<<"Connected!"<<endl<<endl;

    DWORD ThreadID 
= 0;
    
//创建线程,传入参数为SOCKET
    HANDLE ThreadHandle = ::CreateThread(NULL,0,ReceiveThread,&s,0,&ThreadID);
    ::CloseHandle(ThreadHandle);

    
//发送数据
    while(1)
    {
        
char cData[1024= {0};
        cin.getline(cData, 
1024);
        ::send(s, cData, (
int)strlen(cData)+10);
    }

    
//关闭套接字
    ::closesocket(s);

    
//ws2.dll的收尾工作
    ::WSACleanup();
    
return 0;
}

DWORD CALLBACK ReceiveThread(
void* p) 
{
    
while(TRUE)
    {
        
char cBuffer[1024= {0};
        
if(SOCKET_ERROR == ::recv(*(SOCKET*)p, cBuffer, 10240))
        {
            cout
<<"\nServer Closed!"<<endl;
            
break;
        }
        cout
<<"******"<<cBuffer<<endl;
    }

    
return 0;
}


posted on 2009-07-02 20:17 大胖 阅读(307) 评论(0)  编辑 收藏 引用 所属分类: Win32

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理