把它们封装一下,正在研究这个东西。 #ifndef _MYSOCKET_H_ #define _MYSOCKET_H_ #include <WinSock2.h> #include "Exception.h" class CMySocket { public: CMySocket(int type); void Bind(); inline int Sendto(char FAR* buf, int len, int flags, struct sockaddr FAR* to, int FAR* tolen); inline int Sendto(char FAR* buf, int len, int flags, const IpAddress& ipAddress); inline int Recvfrom(char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen); inline int Recvfrom(char FAR* buf, int len, int flags, const IpAddress& ipAddress); protected: SOCKET m_socket; }; class IpAddress { public: IpAddress() { memset(&address, 0, sizeof address); address.sin_family = AF_INET; address.sin_addr.S_un.S_addr = htonl(INADDR_ANY); } IpAddress(const char* ip, DWORD port) { ASSERT(ip != NULL); address.sin_family = AF_INET; address.sin_addr.S_un.S_addr = inet_addr(serverip); address.sin_port = htons(port); } void SetIp(const char* ip) { address.sin_addr.S_un.S_addr = inet_addr(ip); } void SetPort(DWORD port) { address.sin_port = htons(port); } void SetAddress(const char* ip, DWORD port) { ASSERT(ip != NULL); address.sin_addr.S_un.S_addr = inet_addr(ip); address.sin_port = htons(port); } const socketaddr_in* operator&() { return &address; } // const socketaddr socketaddr_in& GetRawElem() { return address; } private: socketaddr_in address; }; #endif #include "stdafx.h" #include "MySocket.h" #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib") CMySocket::CMySocket(int type) { 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)); } m_socket = socket(AF_INET, type, 0); if (m_socket < 0) { printf("create socket error"); throw Exception(""); } } void CMySocket::Bind() { sockaddr_in sin; sin.sin_addr.S_un.S_addr = INADDR_ANY; sin.sin_family = AF_INET; sin.sin_port = 0; if (bind(m_socket, (struct sockaddr*)&sin, sizeof(sin)) < 0) throw Exception("bind error"); } int CMySocket::Sendto(char FAR* buf, int len, int flags, struct sockaddr FAR* to, int FAR* tolen) { ASSERT((buf != NULL) && (to != NULL)); return sendto(m_socket, buf, len, flags, to, tolen); } int CMySocket::Sendto(char FAR* buf, int len, int flags, const IpAddress& ipAddress) { return sendto(m_socket, buf, len, flags, &ipAddress, sizeof(sockaddr_in)); } int CMySocket::Recvfrom(char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen) { ASSERT((buf != NULL) && (from != NULL)); return recvfrom(m_socket, buf, len, flags, from, fromlen); } int CMySocket::Recvfrom(char FAR* buf, int len, int flags, const IpAddress& ipAddress) { return recvfrom(m_socket, buf, len, flags, &ipAddress, sizeof(sockaddr_in)); } #include "stdafx.h" #include "Message.h" #include "MySocket.h" #include "ServerSocket.h" #include <cassert> #define SERVER_PORT 2280 class CServerSocket; class ClientSocket : public CMySocket { public: stUserListNode* GetUser(const char *username); void ConnectToServer(const char *username, const char *serverip); bool SendMessageTo(const char *UserName, const char *Message); private: UserList m_userList; CServerSocket* m_ServerSocket; }; stUserListNode* ClientSocket::GetUser(const char *username) { for(UserList::iterator UserIterator = m_userList.begin(); UserIterator != m_userList.end(); ++UserIterator) { if( strcmp( ((*UserIterator)->userName), username) == 0 ) return *UserIterator; } throw Exception("not find this user"); } void ClientSocket::ConnectToServer(const char *username, const char *serverip) { IpAddress serverAddr(serverip, SERVER_PORT); stMessage sendbuf; sendbuf.msgType = LOGIN_MSG; strncpy(sendbuf.msgBody.loginmember.userName, username, 10); Sendto((const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&serverAddr,sizeof(serverAddr)); int usercount; int fromlen = sizeof(serverAddr); int iread = Recvfrom( (char *)&usercount, sizeof(int), 0, (sockaddr *)&serverAddr, &fromlen); if(iread <= 0) { throw Exception("Login error/n"); } // 登录到服务端后,接收服务端发来的已经登录的用户的信息 cout<<"Have "<<usercount<<" users logined server:"<<endl; for(int i = 0;i<usercount;i++) { stUserListNode *node = new stUserListNode; Recvfrom( (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&serverAddr, &fromlen); m_userList.push_back(node); cout << "Username:" << node->userName << endl; in_addr tmp; tmp.S_un.S_addr = htonl(node->ip); cout << "UserIP:" << inet_ntoa(tmp) << endl; cout << "UserPort:" << node->port << endl; cout << "" << endl; } } /*process user login*/ void ProcLogin() { ; } bool ClientSocket::SendMessageTo(const char *UserName, const char *Message) { char realmessage[256]; unsigned int UserIP; unsigned short UserPort; bool FindUser = false; for(UserList::iterator UserIterator = m_userList.begin(); UserIterator != m_userList.end(); ++UserIterator) { if(strcmp(((*UserIterator)->userName), UserName) == 0) { UserIP = (*UserIterator)->ip; UserPort = (*UserIterator)->port; FindUser = true; break; } } if(!FindUser) return false; strcpy(realmessage, Message); for(int i = 0; i < MAXRETRY; i++) { RecvedACK = false; IpAddress serverAddr(UserIP, UserPort); stP2PMessage MessageHead; MessageHead.iMessageType = P2PMESSAGE; MessageHead.iStringLen = (int)strlen(realmessage)+1; int isend = sendto(m_ServerSocket, (const char *)&MessageHead, sizeof(MessageHead), 0, (const sockaddr*)&serverAddr, sizeof(serverAddr)); isend = sendto(m_ServerSocket, (const char *)&realmessage, MessageHead.iStringLen, 0, (const sockaddr*)&serverAddr, sizeof(serverAddr)); // 等待接收线程将此标记修改 for(int j = 0; j < 10; j++) { if(RecvedACK) return true; else Sleep(300); } // 没有接收到目标主机的回应,认为目标主机的端口映射没有 // 打开,那么发送请求信息给服务器,要服务器告诉目标主机 // 打开映射端口(UDP打洞) sockaddr_in server; server.sin_addr.S_un.S_addr = inet_addr(ServerIP); server.sin_family = AF_INET; server.sin_port = htons(SERVER_PORT); stMessage transMessage; transMessage.iMessageType = P2PTRANS; strcpy(transMessage.message.translatemessage.userName, UserName); sendto(m_ServerSocket, (const char*)&transMessage, sizeof(transMessage), 0, (const sockaddr*)&server, sizeof(server)); Sleep(100);// 等待对方先发送信息。 } return false; } // 接受消息线程 DWORD WINAPI RecvThreadProc(LPVOID lpParameter) { sockaddr_in serverAddr; int sinlen = sizeof(serverAddr); stP2PMessage recvbuf; CServerSocket *pServerSocket = (CServerSocket* )lpParameter; for(;;) { int iread = recvfrom(*serverSocket, (char *)&recvbuf, sizeof(recvbuf), 0, (sockaddr *)&serverAddr, &sinlen); if(iread<=0) { printf("recv error/n"); continue; } switch(recvbuf.iMessageType) { case P2PMESSAGE: { // 接收到P2P的消息 char *comemessage= new char[recvbuf.iStringLen]; int iread1 = recvfrom(*pServerSocket, comemessage, 256, 0, (sockaddr *)&serverAddr, &sinlen); comemessage[iread1-1] = '/0'; if(iread1 <= 0) throw Exception("Recv Message Error/n"); else { printf("Recv a Message:%s/n",comemessage); stP2PMessage sendbuf; sendbuf.iMessageType = P2PMESSAGEACK; sendto(*pServerSocket, (const char*)&sendbuf, sizeof(sendbuf), 0, (const sockaddr*)&serverAddr, sizeof(serverAddr)); } delete []comemessage; break; } case P2PSOMEONEWANTTOCALLYOU: { // 接收到打洞命令,向指定的IP地址打洞 printf("Recv p2someonewanttocallyou data/n"); sockaddr_in serverAddr; serverAddr.sin_addr.S_un.S_addr = htonl(recvbuf.iStringLen); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(recvbuf.Port); // UDP hole punching stP2PMessage message; message.iMessageType = P2PTRASH; sendto(*pServerSocket, (const char *)&message, sizeof(message), 0, (const sockaddr*)&serverAddr, sizeof(serverAddr)); break; } case P2PMESSAGEACK: { // 发送消息的应答 RecvedACK = true; break; } case P2PTRASH: { // 对方发送的打洞消息,忽略掉。 //do nothing ... printf("Recv p2ptrash data/n"); break; } case GETALLUSER: { int usercount; int fromlen = sizeof(serverAddr); int iread = recvfrom(*pServerSocket, (char *)&usercount, sizeof(int), 0, (sockaddr *)&serverAddr, &fromlen); if(iread<=0) { throw Exception("Login error/n"); } ClientList.clear(); cout<<"Have "<<usercount<<" users logined server:"<<endl; for(int i = 0;i<usercount;i++) { stUserListNode *node = new stUserListNode; recvfrom(*pServerSocket, (char*)node, sizeof(stUserListNode), 0, (sockaddr *)&serverAddr, &fromlen); ClientList.push_back(node); cout<<"Username:"<<node->userName<<endl; in_addr tmp; tmp.S_un.S_addr = htonl(node->ip); cout<<"UserIP:"<<inet_ntoa(tmp)<<endl; cout<<"UserPort:"<<node->port<<endl; cout<<""<<endl; } break; } } } } #include "stdafx.h" #include "MySocket.h" class CServerSocket : public CMySocket { public: stUserListNode GetUser(const char* userName); int Run(); private: UserList m_userList; }; stUserListNode* CServerSocket::GetUser(const char* userName) { for (UserList::iterator iter = m_userList; iter != m_userList.end(); ++iter) { if (strcmp((*iter)->userName, userName) == 0) return *iter; } throw Exception("not find this user"); } int CServerSocket::Run() { try { //InitWinSock(); //SOCKET PrimaryUDP; m_socket = mksock(SOCK_DGRAM); sockaddr_in local; local.sin_family=AF_INET; local.sin_port= htons(SERVER_PORT); local.sin_addr.s_addr = htonl(INADDR_ANY); int nResult=bind(m_socket,(sockaddr*)&local,sizeof(sockaddr)); if(nResult == SOCKET_ERROR) throw Exception("bind error"); sockaddr_in sender; stMessage recvbuf; memset(&recvbuf,0,sizeof(stMessage)); // 开始主循环. // 主循环负责下面几件事情: // 一:读取客户端登陆和登出消息,记录客户列表 // 二:转发客户p2p请求 for(;;) { int dwSender = sizeof(sender); int ret = Recvfrom( (char *)&recvbuf, sizeof(stMessage), 0, (sockaddr *)&sender, &dwSender); 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((const char*)&nodecount, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender)); for(UserList::iterator UserIterator=ClientList.begin(); UserIterator!=ClientList.end(); ++UserIterator) { Sendto((const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&sender, sizeof(sender)); } break; } case LOGOUT: { // 将此客户信息删除 printf("has a user logout : %s/n", recvbuf.message.logoutmember.userName); UserList::iterator removeiterator = NULL; for(UserList::iterator UserIterator=ClientList.begin(); UserIterator!=ClientList.end(); ++UserIterator) { if( strcmp( ((*UserIterator)->userName), recvbuf.message.logoutmember.userName) == 0 ) { removeiterator = UserIterator; break; } } if(removeiterator != NULL) 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);//"TO" sockaddr_in remote; remote.sin_family=AF_INET; remote.sin_port= htons(node.port); remote.sin_addr.s_addr = htonl(node.ip); 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); Sendto((const char*)&transMessage, sizeof(transMessage), 0, (const sockaddr *)&remote, sizeof(remote)); break; } case GETALLUSER: { int command = GETALLUSER; Sendto((const char*)&command, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender)); int nodecount = (int)ClientList.size(); Sendto((const char*)&nodecount, sizeof(int), 0, (const sockaddr*)&sender, sizeof(sender)); for(UserList::iterator UserIterator=ClientList.begin(); UserIterator!=ClientList.end(); ++UserIterator) { Sendto((const char*)(*UserIterator), sizeof(stUserListNode), 0, (const sockaddr*)&sender, sizeof(sender)); } break; } } } } } catch(Exception &e) { printf(e.GetMessage()); return 1; } return 0; } #ifndef _MESSAGE_H_ #define _MESSAGE_H_ #include <list> using namespace std; enum MSG_TYPE { /*控制消息*/ LOGIN_MSG, LOGOUT_MSG, P2PTRANS_MSG, GETUSER_MSG, /*数据消息*/ DATA_MSG }; struct stLoginMessage { char userName[10]; char password[10]; }; // Client注销时发送的消息 struct stLogoutMessage { char userName[10]; }; // Client向服务器请求另外一个Client(userName)向自己方向发送UDP打洞消息 struct stP2PTranslate { char userName[10]; }; // 客户节点信息 struct stUserListNode { char userName[10]; unsigned int ip; unsigned short port; }; union MSG_BODY { stLoginMessage loginmember; stLogoutMessage logoutmember; stP2PTranslate translatemessage; stUserListNode user; }; struct stMessage { MSG_TYPE msgType; MSG_BODY msgBody; }; //====================================== // 下面的协议用于客户端之间的通信 //====================================== #define P2PMESSAGE 100 // 发送消息 #define P2PMESSAGEACK 101 // 收到消息的应答 #define P2PSOMEONEWANTTOCALLYOU 102 // 服务器向客户端发送的消息 // 希望此客户端发送一个UDP打洞包 #define P2PTRASH 103 // 客户端发送的打洞包,接收端应该忽略此消息 // 客户端之间发送消息格式 struct stP2PMessage { int iMessageType; int iStringLen; // or IP address unsigned short Port; }; using namespace std; typedef list<stUserListNode *> UserList; #endif #ifndef _EXCEPTION_H_ #define _EXCEPTION_H_ #define EXCEPTION_MESSAGE_MAXLEN 256 #include "string.h" class Exception { private: char m_ExceptionMessage[EXCEPTION_MESSAGE_MAXLEN]; public: Exception(char *msg) { strncpy(m_ExceptionMessage, msg, EXCEPTION_MESSAGE_MAXLEN); } char *GetMessage() { return m_ExceptionMessage; } }; #endif