欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

p2p学习记录

程序员文章站 2022-03-04 23:28:34
...
/* P2P 程序服务端
 * 
 * 文件名:P2PServer.c
 *
 * 日期:2004-5-21
 *
 * 作者:shootingstars([email protected])
 *
 */
#pragma comment(lib, "ws2_32.lib")

#include "windows.h"
#include "..\proto.h"
#include "..\Exception.h"

UserList ClientList;

void InitWinSock()//自定义初始化函数,套接字服务初始化,任何一个进程使用套接字服务时必须有的操作
{
	WSADATA wsaData;

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("Windows sockets 2.2 startup");
		throw Exception("");
	}
	else{
		printf("Using %s (Status: %s)\n",
			wsaData.szDescription, wsaData.szSystemStatus);
		printf("with API versions %d.%d to %d.%d\n\n",
			LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion),
			LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));
		
	}
}

SOCKET mksock(int type)
{
	SOCKET sock = socket(AF_INET, type, 0);////自定义套接字创建函数,创建套接字,AF_INNET(使用IPV4地址),type(套接字类型,p2p一般使用udp),指定的协议类型(0代表默认),正常执行会返回套接字地址
	if (sock < 0)
	{
        printf("create socket error");
		throw Exception("");
	}
	return sock;
}

stUserListNode GetUser(char *username)//链表查找用户名,存在返回节点指针
{
	for(UserList::iterator UserIterator=ClientList.begin();
						UserIterator!=ClientList.end();
							++UserIterator)
	{
		if( strcmp( ((*UserIterator)->userName), username) == 0 )
			return *(*UserIterator);
	}
	throw Exception("not find this user");
}

int main(int argc, char* argv[])
{
	try{
		InitWinSock();
		
		SOCKET PrimaryUDP;
		PrimaryUDP = mksock(SOCK_DGRAM);//SOCK_DGRAM udp套接字类型

		sockaddr_in local;//
		local.sin_family=AF_INET;
		local.sin_port= htons(SERVER_PORT); 
		local.sin_addr.s_addr = htonl(INADDR_ANY);//套接字地址初始化,INADDR_ANY代表本机地址
		int nResult=bind(PrimaryUDP,(sockaddr*)&local,sizeof(sockaddr));//PrimaryUDP套接字唯一标示,local的地址结构由它的地址类型决定sockaddr_in对应IPV4,最后一个参数是地址长度
		if(nResult==SOCKET_ERROR)
			throw Exception("bind error");

		sockaddr_in sender;//sender用于接收client登陆的用户地址信息,用于存储服务端发送消息的目标地址
		stMessage recvbuf;//定义cilent发送数据的格式,意味着服务器和客户端支持同一种协议,p2p使用私有协议
		memset(&recvbuf,0,sizeof(stMessage));

		// 开始主循环.
		// 主循环负责下面几件事情:
		// 一:读取客户端登陆和登出消息,记录客户列表
		// 二:转发客户p2p请求
		for(;;)
		{
			int dwSender = sizeof(sender);
			int ret = recvfrom(PrimaryUDP, (char *)&recvbuf, sizeof(stMessage), 0, (sockaddr *)&sender, &dwSender);//循环接收对应端口的信息,客户端使用事先定义的端口进行登入登出 p2p请求
			if(ret <= 0)
			{
				printf("recv error");
				continue;
			}
			else
			{
				int messageType = recvbuf.iMessageType;
				switch(messageType){
				case LOGIN:
					{
						//  将这个用户的信息记录到用户列表中
						printf("has a user login : %s\n", recvbuf.message.loginmember.userName);
						stUserListNode *currentuser = new stUserListNode();
						strcpy(currentuser->userName, recvbuf.message.loginmember.userName);
						currentuser->ip = ntohl(sender.sin_addr.S_un.S_addr);
						currentuser->port = ntohs(sender.sin_port);
						
						ClientList.push_back(currentuser);

						// 发送已经登陆的客户信息
						int nodecount = (int)ClientList.size();
						sendto(PrimaryUDP, (const char*)&nodecount, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender));
						for(UserList::iterator UserIterator=ClientList.begin();
								UserIterator!=ClientList.end();
								++UserIterator)
						{
							sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&sender, sizeof(sender)); 
						}//将所有在线用户的信息发送给新登陆的client

						break;
					}
				case LOGOUT:
					{
						// 将此客户信息删除
						printf("has a user logout : %s\n", recvbuf.message.logoutmember.userName);
						UserList::iterator removeiterator = ClientList.begin();
						int flag = 1;
						for(UserList::iterator UserIterator=ClientList.begin();
							UserIterator!=ClientList.end();
							++UserIterator)
						{
							if( strcmp( ((*UserIterator)->userName), recvbuf.message.logoutmember.userName) == 0 )
							{
								removeiterator = UserIterator;
								flag = 0;
								break;
							}
						}
						if(flag==0&&(removeiterator!=ClientList.begin()))//46995
							ClientList.remove(*removeiterator);//移除用户信息
						break;
					}
				case P2PTRANS:
					{
						// 某个客户希望服务端向另外一个客户发送一个打洞消息
						printf("%s wants to p2p %s\n",inet_ntoa(sender.sin_addr),recvbuf.message.translatemessage.userName);
						stUserListNode node = GetUser(recvbuf.message.translatemessage.userName);
						sockaddr_in remote;
						remote.sin_family=AF_INET;
						remote.sin_port= htons(node.port); 
						remote.sin_addr.s_addr = htonl(node.ip);//通过查找client消息结构体里存在的传输用户名,获取相应的地址和端口信息

						in_addr tmp;
						tmp.S_un.S_addr = htonl(node.ip);
						printf("the address is %s,and port is %d\n",inet_ntoa(tmp), node.port);

						stP2PMessage transMessage;
						transMessage.iMessageType = P2PSOMEONEWANTTOCALLYOU;
						transMessage.iStringLen = ntohl(sender.sin_addr.S_un.S_addr);
						transMessage.Port = ntohs(sender.sin_port);//将请求p2p通信的客户端的公网ip,端口至于发送buf中
                        
						sendto(PrimaryUDP,(const char*)&transMessage, sizeof(transMessage), 0, (const sockaddr *)&remote, sizeof(remote));//告知被请求p2p通信的客户端向对应地址端口的client发送udp打洞包

						break;
					}
				
				case GETALLUSER:
					{
						int command = GETALLUSER;
						sendto(PrimaryUDP, (const char*)&command, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender));//查询所有用户请求响应

						int nodecount = (int)ClientList.size();
						sendto(PrimaryUDP, (const char*)&nodecount, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender));//发送所有在线用户数目

						for(UserList::iterator UserIterator=ClientList.begin();
								UserIterator!=ClientList.end();
								++UserIterator)
						{
							sendto(PrimaryUDP, (const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&sender, sizeof(sender)); //遍历,循环发送所有用户信息
						}
						break;
					}
				}
			}
		}

	}
	catch(Exception &e)//捕获异常,发生异常就执行下面的语句
	{
		printf(e.GetMessage());
		return 1;
	}

	return 0;
}

转自:http://www.ppcn.net/p2ptech.html

相关标签: P2P