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

来源:互联网 发布:宝宝成长日记软件 编辑:程序博客网 时间:2024/05/16 19:44

转载注明出处:http://blog.csdn.net/napolun007/article/details/6050241

这几天忙着搞UDP的socket通信,忙乎了几天终于有点成就了,窃喜下。。。。

如果你不懂内网和外网的区别,不懂局域网和广域网就先熟悉下,再来看程序。我目前的情况是客户端在一个内网上,要连接外网的服务器,外网服务器在收到客户端的请求后,反馈信息给客户端。

请注意是UDP,不是TCP。

先引入内网和外网的一些些小知识:

如我内网的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));

这几个值不要修改,照原样反馈给客户端,就没问题了,本来是打算把工程都发上来的,可是没找到那里可以添加附件,所以填了代码,如果大家不清楚,可以来问我,我及时给解答


原创粉丝点击