请教关于Socket、OICQ服务器和多线程的编程(300分)

  • 主题发起人 主题发起人 delphiroad
  • 开始时间 开始时间
D

delphiroad

Unregistered / Unconfirmed
GUEST, unregistred user!
有哪位了解例如OICQ服务器和EMail邮局之类的实现方法。讲一下让我了解一下吧!
我现在要写的系统和OICQ也有点不同(以下它指我要写的系统):
1、每人有一个账户;
2、有点类似于邮件发送,两个客户端之间的所有数据传输均要通过服务器中转,并且所有数据均在服务器上的后台数据库中有储存副本;
3、客户A向客户B发送数据时,B并不需要在线;
4、用Socket来实现;
5、要求最高能同时承受10000个客户连接。
其实上述过程更像Email,但我又不想用普通的EMail邮局,原因主要是:我认为EMail是明文传输,保密性不强。
我的问题有许多个(以前没有作过网络编程,大伙别笑我了,帮帮忙才是啊!):
1、可否用Socket来实现?通过搜索,知道一种完成端口的技术挺好,但我不知道什么是完成端口,哪位能解释一下吗?
2、用什么技术能够达到同时承受成千上万的连接(所以我才提到QICQ,它是如何应付这么多客户连接的?)?
3、用Win2000作为服务器可不可以?用Delphi来实现服务器端程序可不可以?
4、这个服务器将放在Intenet上,由于要求所有客户的数据绝对不能丢失,怎样达到如此高的安全要求?
我认为这个服务器功能比较单一,只提供一个假设为2121的端口,是不是可以完全避免黑客侵入。如果不能完全避免,还必须做那些工作来保护这台服务器(尤其是最重要的后台数据库中的数据)
5、如果采用Linux是否会安全些或效率高些,我没有接触过Linux,因此我更倾向于用Win2k作为操作系统,并且能用Delphi来做就更好了。
6、是不是将后台服务器(如sql server)放在另一台机上,接收服务和发送服务也分开,即最少用3台计算机来构成这个服务器,是不是效率和安全性都高些?
最后一个问题,OICQ服务器运行在什么操作系统上,是用什么语言写的?

问题很长,能坚持看完已经很感谢您了!
 
你下载MyIcq的源码分析吧。到www.vckbase.com下载。是VC++编写的
 
1,看你的要求,只能用TCP解决,原因很简单,你需要保证数据正确性和完整性
2,多服务器集群,负载均衡,
3,W2K SERVER可以做服务器
4,这个是你设计服务器成败最关键的因素只一
5,LINUX和WINDOWS安全行我感觉差不太远、
6,当然,数据服务器放在内网的机器上最安全
 
谢谢无忌兄,我刚刚还在以你的名字来搜索帖子呢!
能否进QQ再次深入的请教你?当然如果你现在没空不要紧,不是很急的项目!
 
如何像OICQ那样,可以动态指定端口呢?我现在用NMUDP,结果不能动态指定端口,
谁知道可以告诉我吗?要分的话,请提出,我另起贴。
 
动态端口不是指定的,是网关和局域网内机器自动生成的映射端口,这个不用太过深入研究,只要Server端取得FromIP和映射端口,然后根据这个返回数据就可以了。
 
我也想知道成千上萬用戶同時在線的情況。
 
使用UDP可以模拟虚拟在线,可以有效的减小服务器的负担,
如果保证数据的完整性和安全性,需要附加的控制
QQ用的是UDP来传送数据的,据它自己所说,
一是减少较差的网络状况带来的影响,另一个是为了负担更大的用户量

如果采用TCP,这么高的用户量,最好有负载均衡控制,
有多台服务器分担负荷,而且服务器之间也需要进行数据交换
(有点像电话交换机)
 
决定使用“完成端口”,我已经作过实验了,但是在VC6下可以,在Delphi中就不行了,请
高手帮我看看这个单元,请注意其中注释:
问题状况是:问题出在客户端可以连接到服务器,但发送数据和断开时没有反应。

unit SvListen;

interface

uses
Windows,WinSock2,Dialogs;
//高手请注意:由于Delphi6中只有Winsock1的pas单元,我自己在原来Winsock.pas的基础
//上添加了几个声明(主要是WSAAceept、WSARecv、WSASend和几个Record)形成了这个
//Winsock2.pas

Const
DATA_BUFSIZE=10;
RECV_POSTED=0;
SEND_POSTED=1;

Type
LPPER_HANDLE_DATA = ^PER_HANDLE_DATA;
PER_HANDLE_DATA=record
Socket:TSocket;
end;

LPPER_IO_OPERATION_DATA = ^PER_IO_OPERATION_DATA;
PER_IO_OPERATION_DATA=record
Overlapped:OVERLAPPED;
DataBuf:WSABUF;
Buffer:array[0..DATA_BUFSIZE] of CHAR;
OperationType:byte;
// other useful information
end;

function StartServer:Integer;//开启服务

function ListenThread(params:pointer):DWORD;stdcall;//监听线程

function SerVerWorkerThread(params:pointer):DWORD;stdcall;//工作线程

var
hIocp:THANDLE;//完成端口句柄
sizeofPHD,sizeofPID:Integer;
SystemInfo:SYSTEM_INFO;//作用:获得Cpu个数
Listen:TSOCKET;//监听套接字
InternetAddr:sockaddr_in;
IsExit:boolean;
IsRunning:boolean;

implementation

function StartServer:Integer;
var
v_WSAData:WSADATA;
I:Cardinal;
ThreadID,ThreadHandle:Cardinal;
begin
//Step1初始化WinSock2
if WSAStartup(MAKEWORD(2,2),v_WSAData)<>0 then
begin
StartServer:=1;//不能初始化Winsock2
Exit;
end;

sizeofPHD:=sizeof(PER_HANDLE_DATA);
sizeofPID:=sizeof(PER_IO_OPERATION_DATA);

//Step2创建完成端口(先判断系统处理器个数)
GetSystemInfo(SystemInfo);
hIocp:=CreateIoCompletionPort(INVALID_HANDLE_VALUE,0, 0, SystemInfo.dwNumberOfProcessors);
//这里最后一个参数是每次有效的最大工作线程的数量,设为0时表示等于处理器个数
if hIocp=0 then
begin
StartServer:=2;//不能创建完全端口
Exit;
end;

//Step3创建工作线程
for I:=0 to SystemInfo.dwNumberOfProcessors-1 do
begin
ThreadHandle:=CreateThread(nil,0,@SerVerWorkerThread,Pointer(hIocp),0,ThreadID);
CloseHandle(ThreadHandle);
end;

//Step4创建侦听套接字
Listen:=WSASocket(AF_INET,SOCK_STREAM,0,nil,0,WSA_FLAG_OVERLAPPED);
InternetAddr.sin_family:=AF_INET;
InternetAddr.sin_addr.s_addr:=htonl(INADDR_ANY);
InternetAddr.sin_port:=htons(5150);
bind(Listen,InternetAddr,sizeof(InternetAddr));
//Step5启动侦听线程
ThreadHandle:=CreateThread(nil,0,@ListenThread,nil,0,ThreadID);
if ThreadHandle=0 then
begin
StartServer:=5; //侦听线程启动失败
Exit;
end;
CloseHandle(ThreadHandle);
//ListenThread(nil);
IsRunning:=True;
StartServer:=0;//开始工作了
end;

//////////监听线程/////////////////////////
function ListenThread(params:pointer):DWORD;stdcall;
var
PerHandleData:LPPER_HANDLE_DATA;
PerIoData:LPPER_IO_OPERATION_DATA;
RecvBytes,Flags:Integer;
Accept:TSOCKET;
begin
WinSock2.listen(Listen,5);
IsExit:=FALSE;
while(TRUE) do
begin
if IsExit=TRUE then
begin
break;
end;
Accept:=WSAAccept(Listen,nil,nil,nil,0);
//为每个客户连接分配一个每连接数据块
GetMem(PerHandleData,sizeofPHD);
//printf("Socket number %d connected/n",Accept);//原文的这句我不知道是做什么用的
PerHandleData.Socket:=Accept;
//将套节字与完成端口联系
CreateIoCompletionPort(Accept,hIocp,DWORD(PerHandleData),0);
//准备接收数据
Flags:=0;
GetMem(PerIoData,sizeofPID);
PerIoData.DataBuf.len:=DATA_BUFSIZE;
PerIoData.DataBuf.buf:=PerIoData.Buffer;
PerIoData.OperationType:=RECV_POSTED;
WSARecv(Accept ,PerIoData.DataBuf,1,RecvBytes,
Flags,_OVERLAPPED(PerIoData.Overlapped),nil);
//这句命名执行了,但在工作线程中就是收不到??????????????
end;
//Step6结束
IsRunning:=False;
WSACleanup();
end;

///////////工作线程///////////////////////
function SerVerWorkerThread(params:pointer):DWORD;stdcall;
var
PerHandleData:LPPER_HANDLE_DATA;
PerIoData:LPPER_IO_OPERATION_DATA;
BytesTransferred:DWORD;
RecvBytes,Flags:Integer;
ret:LongBool;
begin
while (True) do
begin
ret:=GetQueuedCompletionStatus(hIocp,BytesTransferred,DWORD(PerHandleData),
POVERLAPPED(PerIoData),INFINITE);
//执行到以上这一句,该线程就挂起了,不再动了,导致以下代码永不执行!!!!!!!??????
if not ret then
begin
//读或写操作以失败告终
continue;
end;
if(BytesTransferred = 0) and
((PerIoData.OperationType = RECV_POSTED) or
(PerIoData.OperationType =SEND_POSTED)) then
begin
//客户端已经断开套节字,释放本地套节字
closesocket(PerHandleData.Socket);
//执行了closesocket之后,所有操作都将完成
FreeMem(PerHandleData,sizeofPHD);
FreeMem(PerIoData,sizeofPID);
//GlobalFree(PerHandleData);
//GlobalFree(PerIoData);
IsExit:=TRUE;//这是我做实验用的,真实的程序肯定不是在这里终结
continue;
end;
if PerIoData.OperationType = RECV_POSTED then
begin
//读取,这也是实验用的,真实的程序还有很多处理,包括数据库操作
showmessage(PerIoData.DataBuf.buf);
//fputs(PerIoData->DataBuf.buf,fp);
end;
Flags:=0;
ZeroMemory(@PerIoData.Overlapped,sizeof(OVERLAPPED));
PerIoData.DataBuf.len:=DATA_BUFSIZE;
PerIoData.DataBuf.buf:=PerIoData.Buffer;
PerIoData.OperationType:=RECV_POSTED;
WSARecv(PerHandleData.Socket ,PerIoData.DataBuf,1,RecvBytes,
Flags,_OVERLAPPED(PerIoData.Overlapped),nil);
end;
end;

end.
 
去掉stdcall就完全正常了。
 
3个服务器如何够用?需要数十、百的服务器。许多数据库管理系统为都可为你实现负载均衡。何必自己动手呢?
 
见鬼,OICQ 是 P2P 的。
 
QQ服务器只做了登陆工作和维持连接工作,客户端的通讯80%是点对点的UDP,20%是通过服务器转发的,因此一台服务器可以维持大概100000个左右的在线用户,我做的虚拟城市(原月光宝盒)已经实现了这样的负载,估计不超过半个月就会发布公开版本,到时候你可以看看
 
不以QQ做例子了,毕竟和我的需求太多不同。
以大富翁做例子反而相近,可以想像,有时候我们很多人一起发贴(发贴应该是往数据库里
增加记录吧),我听说大富翁用的也是sqlserver,怎么应付多人一起增加记录呢?
大富翁的管理员在不在,讲一讲吧!
我说的3台服务器只是一个例子,当然客户连接多了,服务器也要增加。
 
delphiroad: 是不是去掉stdcall就好了?也没有一个话。

大富翁使用http协议,应该简单一些,只需要构造业务层。用什么IO模型都是由所使用的
web服务器来决定的。至于多人同时增加记录这和一般的C/S模式没什么区别呀?
 
关注 一下 !
帮不上忙 但我也想知道讨论结果
 
现在完成端口我已经做好了,不过由于还有其它问题未解决,因此暂时不贴出来了
(好像挺多人想看源代码的),其实完成端口也不复杂的。可下载这本书看:
http://www.deit.ecnu.edu.cn/ebook/WinNetPro/winntpro.htm
windows网络编程
尤其是其中的第八章,对完成端口有完整的介绍。

现在解决新问题:TCP协议的保证传输完整性是如何解释的?
无忌兄,最想听你的意见:我用的是Tcp协议,它能保证传输完整性是不是指:
发送端的发送操作一定能“到达”接收端,否则发送端会返回错误?
注意,这里用的是到达,而不是读取,因此由问多一句:
数据到达本地AFD.Sys所管理的内部缓冲区之后,发送端是否就认为数据已成功到达
接收端?如果接收端不执行读取(如WSARecv等操作),发送端是不是也返回是成功?

另外,发送端发送的数据到达接收端之后,会不会可能存在一种情况,就是:
发送端认为已经成功发送,但接收端所接收的数据已经发生了变异,如果不存在,
我也就不需要任何对数据进行校验了吗?
注意我的需求:数据必须准确无误的到达终点,多一个少一个字节都不行!
发送完成之后发送端提示“数据传输完成”即表示接收端已经成功接收了!
 
好不友好!

强烈建议大家不要理这个楼主。要找张无忌去QQ上找他好了!为什么来破坏纯净的大富翁
论坛!
delphiroad进入了我的黑名单。
 
barton 你喝高了?[?]
 
我怎么会喝高?即使喝高也不会在大富翁胡闹。
本人并不在乎分数,如果在乎分数估计我的分要比现在的分高得多。但我不能容忍这种不
懂起码礼貌的人混迹于大富翁。

这个楼主贴了一段代码来说有什么错,我已经指出来要去掉stdcall。可是这个楼主不理不
睬的。话里话外不希望我们这种菜鸟参与回答。还有一道关于ipx/spx协议检测的题,人家
Pipi的是正解,不给分不说,也是不理不睬。要知道人家Pipi当你师爷爷都绰绰有余!回
你贴子那是你的荣幸!知道吗?狂妄的小子!
 
后退
顶部