您的位置:首页 > 其它

UDP内网和外网连接通信的问题

2016-12-14 18:02 357 查看
如我内网的IP为:192.168.0.2,端口为3200,此时我想和外网的IP:220.120.123.42,端口为23654通信,从客户端发起请求,可以根据外网的IP和端口顺利找到服务器,这是单项通信,可是服务器给内网的机器发就困难了,不以为然的同学请先仔细考虑下再来拍砖。

整个数据流的路途是这样的:

我的内网IP和端口在经过我的网关之后,都会发生变化。可能会变为网关的外网如:124.253.124.12:62145,实际和服务器通信的是网关转换后的地址和端口,也就是说你的内网IP和端口只有网关知道是哪台机器。好了,想清楚了这个就好办多了,上代码给大家看看吧。

 

UDP编程要留意客户端的端口号,一定要注意,这个TCP不同,UDP是不会有长连接和稳定通信渠道的

 

#include <WinSock2.h>

#pragma comment(lib, "ws2_32")

//socket版本号

WSADATA wsaData;

 WORD socketVersion = MAKEWORD(2, 2);

 if (::WSAStartup(socketVersion, &wsaData) != 0)

 {

  TRACE(L"Init socket dll error!");

 }

//ClientUDP.h

private:

 DWORD  mTargetIP;      //// 远程端IP地址(使用主机字节顺序)

 WORD  mTargetPort;// 远程端口号

 WORD  mLocalPort;     // 本地端口号

 BOOL  mIsReceiving;// 正在接收数据的标记

 HANDLE  mRcvThread;// 数据接收线程句柄

 SOCKET  mSckReceiver;// 用于接收的Socket

 SOCKET  mSckSender; // 用于发送的Socket

private:

//创建/销毁用于发送的Socket

 BOOL CreateSender(void);

 void DeleteSender(void);

 // 创建/销毁用于接收的Socket

 BOOL CreateReceiver(void);

 void DeleteReceiver(void);

 void ReceivingLoop(void);// 数据接收循环过程

 static DWORD WINAPI ReceivingThrd(void * pParam); // 接收线程执行体

 // 启动/停止数据接收线程

 BOOL StartReceiving(void);

 void StopReceiving(void);

 void SendData(char* pchar,long length);//发送的数据(内容,长度)

 BOOL GetHostInfo(char * outIP, char * outName = NULL);获取本机信息

//ClientUDP.CPP

BOOL CreateSender(void)

{

 //DeleteSender();

 mSckSender = socket(AF_INET, SOCK_DGRAM, 0);

 if (mSckSender != INVALID_SOCKET)

 {

  BOOL flag = TRUE;

  int retr = setsockopt(mSckSender, SOL_SOCKET, SO_REUSEADDR, 

   (char *) &flag, sizeof(flag));//设置socket为地址复用

  if (retr == SOCKET_ERROR) 

  {

   DeleteReceiver();

   return FALSE;

  }

  int ret = 0;

  sockaddr_in addr;

  memset((char *) &addr, 0, sizeof(addr));

  char ip[20];

  char name[20];

  GetHostInfo(ip,name);//获取本机的IP和用户名

  addr.sin_addr.S_un.S_addr = inet_addr(ip);

  addr.sin_family = AF_INET;

  addr.sin_port = htons(CLIENTPORT);//本机端口,注意该端口一定要和监听的端口是同一端口(接听下面会写)

  ret = bind(mSckSender, (struct sockaddr*) &addr, sizeof(addr));//绑定要发送的socket

  if (ret == SOCKET_ERROR) 

  {

  DeleteSender();

  return FALSE;

  }

   return TRUE;

 }

 return FALSE;



//发送的数据

void SendData(char* pchar,long length)

{

 char* tt = new char[length + 1];

 memset(tt,'/0',length + 1);

 memcpy(tt,pchar,length);

 sockaddr_in remote;

 memset((char *) &remote, 0, sizeof(remote));

 remote.sin_addr.S_un.S_addr = inet_addr(IP_SERVER);//要发送的服务器IP

 remote.sin_family = AF_INET;

 remote.sin_port = htons(USRPORT_SERVER);//服务器的端口

 sendto(mSckSender, tt, length, 0, 

  (sockaddr *) &remote, sizeof(remote));

 DeleteSender();//每次发送要关闭发送socket,我测试过,要是注释掉,下次就不会收到服务器的反馈了

 delete[] tt;

}

 

 

BOOL GetHostInfo(char * outIP, char * outName)

{

 char   name[300];

 if (gethostname(name, 300) == 0)

 {

  if (outName)

  {

   strcpy(outName, name);

  }

  PHOSTENT  hostinfo;

  if ((hostinfo = gethostbyname(name)) != NULL)

  {

   LPCSTR pIP = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);

   strcpy(outIP, pIP);

   return TRUE;

  }

 }

 return FALSE;

}

 

void DeleteSender(void)

{

 if (mSckSender != INVALID_SOCKET)

 {

  closesocket(mSckSender);

  mSckSender = INVALID_SOCKET;

 }

}

//创建接收的socket

BOOL CreateReceiver(void)

{

 DeleteReceiver();

 // 创建一个UDP传输的Socket

 mSckReceiver = socket(AF_INET, SOCK_DGRAM, 0);

 if (mSckReceiver != INVALID_SOCKET)

 {

  // 在Socket上设置参数:允许地址复用

  BOOL flag = TRUE;

  int ret = setsockopt(mSckReceiver, SOL_SOCKET, SO_REUSEADDR, 

   (char *) &flag, sizeof(flag));

  if (ret == SOCKET_ERROR) 

  {

   DeleteReceiver();

   return FALSE;

  }

  // 将Socket绑定到本地端口号上

  SOCKADDR_IN  addr;

  addr.sin_family      = AF_INET;

  addr.sin_addr.s_addr = htonl(INADDR_ANY);

  addr.sin_port        = htons(CLIENTPORT);//一定要把这里的监听端口和发送的设置为同一个端口

  ret = bind(mSckReceiver, (struct sockaddr*) &addr, sizeof(addr));

  if (ret == SOCKET_ERROR) 

  {

   DeleteReceiver();

   return FALSE;

  }

  return TRUE;

 }

 return FALSE;

}

//销毁接收socket

void DeleteReceiver(void)

{

 if (mSckReceiver != INVALID_SOCKET)

 {

  closesocket(mSckReceiver);

  mSckReceiver = INVALID_SOCKET;

 }

}

//开启接收线程

BOOL StartReceiving(void)

{

 // Create socket if necessary

 if (mSckReceiver == INVALID_SOCKET)

 {

  CreateReceiver();

 }

 if (mSckReceiver != INVALID_SOCKET)

 {

  if (mIsReceiving)

  {

   return TRUE;

  }

  DWORD threadID = 0;

  mRcvThread = CreateThread(NULL, 0, ReceivingThrd, 

   this, 0, &threadID);

  return (mRcvThread != NULL);

 }

 return FALSE;

}

// 线程函数执行体:调用本类的ReceivingLoop函数

DWORD WINAPI CUDPClient_oneDlg::ReceivingThrd(void * pParam)

{

 ASSERT(pParam);

 CUDPClient_oneDlg * pController = (CUDPClient_oneDlg*) pParam;

 pController->ReceivingLoop();

 return 0;

}

// 数据接收过程

void CUDPClient_oneDlg::ReceivingLoop(void)

{

 struct sockaddr_in  addr_cli;

 int  addr_cli_len = sizeof(addr_cli);

 char buffer[MAX_PATH] = {'/0'};

 long bytes = 0;

 mIsReceiving = TRUE;

 CString tnote = L"";

 // 等待接收数据

 while (mIsReceiving)

 {    

  int addr_cli_len = sizeof(addr_cli);

  bytes = recvfrom(mSckReceiver, (char *)buffer, MAX_PATH,0, (LPSOCKADDR) &addr_cli, (int *) &addr_cli_len);

  if (bytes == SOCKET_ERROR || bytes == 0)

  {

   // 如果Socket发送错误或者Socket断开,则跳出循环

   mIsReceiving = FALSE;

  }

  else

  {

    CEdit* client1 = (CEdit*)this->GetDlgItem(IDC_EDIT1);

    char * pStr = inet_ntoa(addr_cli.sin_addr);

    

    PTCHAR pszOP = new TCHAR[strlen(pStr)*2 + 1];

    memset(pszOP,'/0',strlen(pStr)*2 + 1);

    MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pStr, (int)strlen(pStr)*2, pszOP, (int)strlen(pStr)*2);

    

    PTCHAR pszContent = new TCHAR[bytes*2 + 1];

    memset(pszContent,'/0',bytes*2 + 1);

    MultiByteToWideChar(CP_ACP, 0, (LPCSTR)buffer, bytes*2, pszContent, bytes*2);

    CString text1;

    text1.Format(L"客户端1的IP:%s,端口:%d,回复的内容:%s",pszOP,addr_cli.sin_port,pszContent);

    tnote += text1 + L"/r/n";

    client1->SetWindowText(tnote);

    delete[] pszOP;

    delete[] pszContent;

    memset(buffer,'/0',strlen(buffer));

  }

 }

}

void CUDPClient_oneDlg::StopReceiving(void)

{

 if (mIsReceiving)

 {

  DeleteReceiver();

  // Make sure the receiving thread has been terminated 

  if (mRcvThread != NULL) 

  {

   WaitForSingleObject(mRcvThread, INFINITE);

   mRcvThread = NULL;

  }

 }

}

发送调用的例子为:

{

         CreateSender();

 char p[] = "connect";

 SendData(p,strlen(p));

}

//UDPServer.h,方法和客户端基本相同

private:

 // 创建/销毁用于接收的Socket

 BOOL CreateReceiver(void);

 void DeleteReceiver(void);

 void ReceivingLoop(void);// 数据接收循环过程

 static DWORD WINAPI ReceivingThrd(void * pParam); // 接收线程执行体

 // 启动/停止数据接收线程

 BOOL StartReceiving(void);

 void StopReceiving(void);

关键在这里

struct sockaddr_in  addr_cli;

bytes = recvfrom(mSckReceiver, (char *)buffer, MAX_PATH,0, (LPSOCKADDR) &addr_cli, (int *) &addr_cli_len);

收到客户端发来的内容后,反馈一定要:

sendto(mSckReceiver,(char*)buffer,strlen(buffer),0,(sockaddr*)&addr_cli,sizeof(addr_cli));

这几个值不要修改,照原样反馈给客户端,就没问题了,本来是打算把工程都发上来的,可是没找到那里可以添加附件,所以填了代码,如果大家不清楚,可以来问我,我及时给解答
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  udp 内网 外网 通信