RT
1.在服务器端,使用网络消息通知模式处理的情况下(wsaasyncselect),
每连接一个客户端,就会生成一个新的 socket来跟客户端进行通讯
而原来创建的socket 则可以继续监听客户端来的连接,
问题来了,在多个客户端连接的情况下,是不是每有一个客户端连接,都得定义一个对应的socket来与客户端进行通讯?
那几千上万个客户端连接时,怎么搞?
2.TCP连接的情况下进行nat穿透的问题(通过客户端的socket进行端口重用的方式实现,setsock()函数重定位端口和地址)。
如果两个客户端的NAT网络类型达不到要求(具体的忘了)时,是不是就不能实现P2P的功能?
1.在服务器端,使用网络消息通知模式处理的情况下(wsaasyncselect),
每连接一个客户端,就会生成一个新的 socket来跟客户端进行通讯
而原来创建的socket 则可以继续监听客户端来的连接,
问题来了,在多个客户端连接的情况下,是不是每有一个客户端连接,都得定义一个对应的socket来与客户端进行通讯?
那几千上万个客户端连接时,怎么搞?
2.TCP连接的情况下进行nat穿透的问题(通过客户端的socket进行端口重用的方式实现,setsock()函数重定位端口和地址)。
如果两个客户端的NAT网络类型达不到要求(具体的忘了)时,是不是就不能实现P2P的功能?
2,tcp穿透很麻烦,udp的稍微容易些。另外跟peer端的网络类型有关,记忆力必须是锥型的才可以。
看来UDP和TCP都一样,不是任何情况下都能穿透的
同一个消息事件同时为所有本访问端所连的socket服务,在事件的参数里传递了足够区分哪个socket的信息了
//与客户端连接后的SOCK所产生的网络事件
procedure TForm1.WMCORRESPONDSOCK(var message: TMessage);
var
ret : integer;
fbuf : array[0..255] of Char;
FClientAdd: sockaddr_in;
begin
aSize := SizeOf(sockaddr_in);
recvfrom(AcceptSock,fbuf,Length(fbuf) * SizeOf(Char),0,Add,aSize);
case WSAGetSelectEvent(message.LParam) of
FD_READ :
begin
ret := recv(AcceptSock,fbuf,256,0);
mmo1.Lines.Add('客户端来的消息 : ' + fbuf);
end;
end;
end;
//监视SOCK 所产生的网络消息事件
procedure TForm1.WMSERVERSOCK(var message: TMessage);
var FdSet : TFDSet;
TimeVal : TTimeVal;
AddSize : integer;
Add: TSockAddrIn;
begin
case WSAGetSelectEvent(message.LParam) of
FD_ACCEPT :
begin
AddSize := sizeof(Add);
AcceptSock := accept(FSock,@Add,@AddSize);
if AcceptSock <> INVALID_SOCKET then
WSAAsyncSelect(AcceptSock,Handle,WM_CORRESPONDSOCK,FD_READ or FD_WRITE or FD_CLOSE);
end;
end;
end;
上面的代码,WMSERVERSOCK 即为服务器端的监听 socket ,响应其它客户端的连接,WMCORRESPONDSOCK消息处理过程中,可以获取是哪个客户端发过来的什么信息之类的东西,但是也不能排除两个或多个客户端同时向服务器端发送数据包,那这时服务器端在处理这个消息时会不会有什么冲突?在事件的参数里传递了足够区分哪个socket的信息了,那请问是什么信息呢?
我也这样考虑过,但可能是我的程序处理有问题,当有第二个客户端连接时,服务器端会收到两次登录的消息,其实只有一个客户端发送了登录信息的数据
猜测消息参数wParam 是socket 的句柄, lParam 是socket 事件
哪个socket,应该是SOCKET sock = (SOCKET) wParam;好像这则说的不错:
http://blog.chinaunix.net/u3/103892/showart_2050171.html
我觉得问题应该就是在AcceptSock这里
WSAGetSelectEvent(msg.wparam);就可以知道当前会话的是哪一个客户端...
多连接的问题解决了
不过还有一个问题,就是关于 通过setsockopt()这个函数,在客户端改变连接的IP和端口号,具体不知道怎么用
procedure WndProc(var AMsg: TMessage);
var
Error: word;
begin
with AMsg do
case Msg of
WM_ASYNCSELECT: // 自定义消息头
begin
Error:= WSAGetSelectError(LParam);
case WSAGetSelectEvent(LParam) of
FD_READ : TCPServerData(WParam); // WParam 为 对应的 Socket
FD_ACCEPT: OpenConnection(WParam, Error);
FD_CLOSE : CloseConnection(WParam, Error);
end;
end;
else
Result:= DefWindowProc(FHandle, Msg, WParam, LParam);
end;
end;
每个客户端进来的消息中,都包含了他的socket,服务器只需要针对它做应答就可以了
在客户端创建 socket 时加了这句,使地址端口重用
var Flag : boolean;
addr : tsockaddrIn;
Flag := true;
setsockopt(FSock,SOL_SOCKET,SO_REUSEADDR,@Flag,sizeof(Boolean));
//并且返回成功
但是我在重新在绑定 Fsock的端口时,返回错误
如:获取到对方的地址后,将自己作为服务器端,重新绑定端口,并开启监听,等待对方连接
addr.sin_family := PF_INET;
addr.sin_port := htons(5236);
addr.sin_addr.s_addr := INADDR_ANY;
bing(FSock,addr,sizeof(addr)); //返回错误
不知道哪位有没有处理过这种情况?
服务器一般单独监听一个辅助打洞的端口 P,假如客户端A去请求打洞去连接B,则通过SERVER的端口P向B传输经过A转换后的公网IP地址和端口等信息, B收到后首先连接server端口P,随便发点消息后马上断开。这样做的目的是让S能知道B经过NAT-B转换后的公网IP和端口号。
B尝试与A的公网IP地址和端口进行connect,根据不同的路由器会有不同的结果,有些路由器在这个操作就能建立连接,大多数路由器对于不请自到的SYN请求包直接丢弃而导致connect失败,但A端的路由器会纪录此次连接的源地址和端口号,为接下来真正的连接做好了准备,这就是所谓的打洞,即B向A打了一个洞,下次A就能直接连接到B刚才使用的端口号了。
客户端B打洞的同时在相同的端口上启动侦听。B在一切准备就绪以后通过与S的【主连接】回复消息“我已经准备好”,S在收到以后将B经过NAT-B转换后的公网IP和端口号告诉给A。
A收到S回复的B的公网IP和端口号等信息以后,开始连接到B公网IP和端口号,由于B曾经尝试连接过A的公网IP地址和端口,NAT-A纪录了此次连接的信息,所以当A主动连接B时,NAT-B会认为是合法的SYN数据,并允许通过,从而直接的TCP连接建立起来了。 以上是网络上说的一种方法,确实可行,LZ可以参考下
网络消息的方式管理服务器端与客户端的连接
我的想法是这样的,只不过不太明白何谓一个主连接,一个协助连接
目前程序的结构就是这样:服务器端(S)建立一个 Socket 用来监听客户端来的连接
每个客户端连接后,就记录客户端的信息,包括IP,端口,对应的通信socket 等等
现在A,B都连接到了S端,那就S端就有三个socket,包括原先的监听socket,分别与客户端A,B连接后的 socketA,socketB ,我现在的做法就是,服务器端收到A要连接B的请求后,从S端返回B的IP和PORT给A,同时,给B发送A的IP和PORT信息。
然后在客户端上用原来连接到 S端的socket 进行监听,并连接
可结果总是不成功!!!!
作为客户端,它原先connect服务器,其本地端口是系统分配的,必须先提取这个参数。以上是我的一些看法,不对之处还请指正。
A得到S告知的B的ip2,可以利用,但是p2是无法使用的,因为p2是B作为客户端的端口,并不是B在p2也侦听了
这与udp是不一样的
tcp要求:B必须在一个特定的端口pb侦听,然后告知S,A才能连ip2的pb端口