UDP内网和外网连接通信的问题
這幾天忙著搞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));
這幾個值不要修改,照原樣反饋給客戶端,就沒問題了,本來是打算把工程都發上來的,可是沒找到那里可以添加附件,所以填了代碼,如果大家不清楚,可以來問我,我及時給解答
總結
以上是生活随笔為你收集整理的UDP内网和外网连接通信的问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中Robot
- 下一篇: java怎样调用DLL方法