服务器分发消息,求服务器压力最小化方案

qscool1987 2012-04-20 01:02:35
前天开始研究网络编程这块,感觉理解起来还是很容易,这多亏了看过windows核心编程这本书。

不多说了,先上代码:
#include "stdafx.h"   
#include <windows.h>
#include <process.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

vector<SOCKET> clientSockets;
/*recive message thread*/
DWORD WINAPI ClientThread(LPVOID lp)
{
SOCKET sock = (SOCKET)lp;//----------
char szbuff[1024];
int ret;
while(1)
{
//perform a blocking recv() call
ret = recv(sock,szbuff,1024,0);
if(ret == 0)
break;
else if(ret == SOCKET_ERROR)
{
cout << "recv() failed " << WSAGetLastError() << endl;
break;
}
szbuff[ret] = '\0';
cout << "RECV:" << szbuff << " " << ret << "bytes" << endl;
}
return 0;
}

/*send message thread*/
DWORD WINAPI SendThread(LPVOID lp)
{
int ret;
string msg;
typedef vector<SOCKET>::iterator iter;
while(1)
{
cin >> msg;
iter beg = clientSockets.begin();
for(;beg != clientSockets.end();beg++)
{
ret = send(*beg,msg.c_str(),msg.length() + 1,0);
if(ret == 0)
break;
else if(ret == SOCKET_ERROR)
{
cout << "send failed " << WSAGetLastError() << endl;
break;
}
}
}
return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
//Initialize Winsock
WSADATA wsd;
if(WSAStartup(MAKEWORD(2,2),&wsd) != 0)
{
cout << "Failled to load Winsock!" << endl;
return 1;
}
//create our listening socket
SOCKET sListen,sClient;
sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sListen == SOCKET_ERROR)
{
cout << "socket() failed " << WSAGetLastError() << endl;
return 1;
}
//Select local interface and bind to it
struct sockaddr_in local,client;
int iport = 5000;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(iport);
local.sin_family = AF_INET;
if(bind(sListen,(SOCKADDR*)&local,sizeof(local)) == SOCKET_ERROR)
{
cout << "bind() failed " << WSAGetLastError() << endl;
return 1;
}
listen(sListen,8);
cout << "server side is in listening status!" << endl;
//Waitfor incoming clients,Once one is detected,create thread and pass
//the handle off to it
int iAddrSize(0);
//HANDLE hThread;
//DWORD dwThreadId,dwSendThreadId;
bool createSendRecvFlag = true;
while(1)
{
iAddrSize = sizeof(client);
sClient = accept(sListen,(SOCKADDR*)&client,&iAddrSize);
if(sClient == INVALID_SOCKET)
{
cout << "accept() failed " << WSAGetLastError() << endl;
break;
}
cout << "Accept client:" << inet_ntoa(client.sin_addr) << endl;
cout << ntohs(client.sin_port) << endl;

/*save all the client sockets*/
clientSockets.push_back(sClient);

DWORD dwThreadId,dwSendThreadId;
if(createSendRecvFlag) //create one send thread for all the clients
{
HANDLE hSendThread = CreateThread(NULL,0,SendThread,
(LPVOID)&clientSockets,0,&dwSendThreadId);
createSendRecvFlag = false;
if(hSendThread == NULL)
{
cout << "CreateThread() failed " << GetLastError() << endl;
break;
}
CloseHandle(hSendThread);
}
//create a recive thread for a client
HANDLE hThread = CreateThread(NULL,0,ClientThread,
(LPVOID)sClient,0,&dwThreadId);
if(hThread == NULL)
{
cout << "CreateThread() failed " << GetLastError() << endl;
break;
}
CloseHandle(hThread);
}
closesocket(sListen);
WSACleanup();
return 0;
}

这是服务器端代码,这里涉及到两点

1.针对于客服端发来的消息怎么处理

2.如何将消息发送给所有的客服端

我的解决办法是,用一个全局变量vector<SOCKET> clientSockets;保存每一个accept来的客服端socket,给每一个客服端socket创建一个接受消息线程,而只创建一个发送消息线程统一给所有客服端发送消息。

这其实也好理解:其一服务器发送给每个客服端的消息是相同的所以只需要一个线程,遍历每一个客服端socket即可,其二客服端发送给服务器端的消息是不同的,所以采用多线程机制,不过这样也不太好,如果客服端过多,那么服务器要管理的线程就会很多,但是如果只建立一个接受线程又会出现消息阻塞情况,有待深入研究这个问题。

下面是客服端代码:
#include "stdafx.h"   
#include <windows.h>
#include <process.h>
#include <iostream>
#include <string>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

/*recive message thread*/
DWORD WINAPI ClientThread(LPVOID lp)
{
SOCKET sock = (SOCKET)lp;
char szbuff[1024];
int ret;
while(1)
{
ret = recv(sock,szbuff,1024,0);
if(ret == 0)
break;
else if(ret == SOCKET_ERROR)
{
cout << "recv() failed " << WSAGetLastError() << endl;
break;
}
szbuff[ret] = '\0';
cout << "recv " << szbuff << " " << ret << "bytes" << endl;
}
return 0;
}
/*send message thread*/
DWORD WINAPI SendThread(LPVOID lp)
{
SOCKET sock = (SOCKET)lp;
string msg;
int ret;
while(1)
{
cin >> msg;
ret = send(sock,msg.c_str(),msg.length() + 1,0);
if(ret == 0)
break;
else if(ret == SOCKET_ERROR)
{
cout << "send failed " << WSAGetLastError() << endl;
break;
}
}
return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd;
SOCKET sClient;
struct sockaddr_in server;
struct hostent *host = NULL;
//load Winsock
if(WSAStartup(MAKEWORD(2,2),&wsd) != 0)
{
cout << "Failled to load Winsock!" << endl;
return 1;
}
//create socket and attempt to connect to the server
sClient = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sClient == INVALID_SOCKET)
{
cout << "socket failed " << WSAGetLastError() << endl;
return 1;
}
string szServer;
u_short iport;
cout << "please input IP address: " << endl;
cin >> szServer;
cout << "please input port number: " << endl;
cin >> iport;
server.sin_addr.s_addr = inet_addr(INADDR_ANY);
server.sin_family = AF_INET;
server.sin_port = htons(iport);
//if the supplied server address isn't in the form
//"aaa.bbb.ccc.ddd" it's a host name,so try to resolve it
if(server.sin_addr.s_addr == INADDR_NONE)
{
host = gethostbyname(szServer.c_str());
if(host == NULL)
{
cout << "unable to resolve server: " << szServer << endl;
return 1;
}
CopyMemory(&server.sin_addr,host->h_addr_list[0],host->h_length);
}

if(connect(sClient,(SOCKADDR*)&server,sizeof(server))==SOCKET_ERROR)
{
cout << "connect failed " << WSAGetLastError() << endl;
return 1;
}
cout << "connect succeed!" << endl;
DWORD dwThreadId,dwSendThreadId;
HANDLE hThread = CreateThread(NULL,0,ClientThread,
(LPVOID)sClient,0,&dwThreadId);
HANDLE hSendThread = CreateThread(NULL,0,SendThread,
(LPVOID)sClient,0,&dwSendThreadId);
if(hThread == NULL)
{
cout << "CreateThread() failed " << GetLastError() << endl;
}
if(hSendThread == NULL)
{
cout << "CreateThread() failed " << GetLastError() << endl;
}
CloseHandle(hThread);
CloseHandle(hSendThread);

//prevent main thread exit;
while(1)
{

}
closesocket(sClient);
WSACleanup();
return 0;
}

客服端就简单多了,就是一个收线程,一个发线程,不过这种问题只适合CS架构。至于服务器转发P2P架构就需要服务器帮助解析客服端地址了,这个想来应该不难,稍后再做研究吧

总体来说,网络编程还是要比较深厚的编程基础的,至少对于多线程要有很好的理解,难度到不大,主要就是理解一些库函数,所以更重要的问题就是设计问题,如何让服务器端的压力最小化,最优化,这点需要长期思考。
如果谁有更好的解决方案请留个言,谢谢了!

...全文
134 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
dickbarry 2012-04-20
  • 打赏
  • 举报
回复
你可以看一下IOCP相关的知识,一定会有所裨益
qscool1987 2012-04-20
  • 打赏
  • 举报
回复
运行客服端输入 192.168.0.2,端口输入 5000
不过这是在我的机子上可以连上,如果要拿去试先用下面这段代码看看电脑的IP地址

if(gethostname (Name, sizeof(Name)) == 0)
{
//如果成功地将本地主机名存放入由name参数指定的缓冲区中
if((hostinfo = gethostbyname(Name)) != NULL)
{
//这是获取主机名,如果获得主机名成功的话,将返回一个指针,指向hostinfo,hostinfo
//为PHOSTENT型的变量,下面即将用到这个结构体
strIP = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
}
}

64,680

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧