文档章节

学习笔记一:TCP与UDP通信协议

小菜先生
 小菜先生
发布于 2015/05/31 19:48
字数 1474
阅读 17
收藏 0

TCP通信测试:

 服务器段程序流程:

TCP通信是面向连接的,所以需要服务器进行监听,客户端发起连接,服务器再进行接受。

1.在使用socket通信时需要是要WinSock库,所以首先调用WSAStartup( DWORD wVersionRequested, LPWSADATA lpWSAData)进行库初始化,成功的话返回TRUE.

 WSADATA WSAData;
if(WSAStartup((MAKEWORD(2,0)), &WSAData) != 0)
{
return FALSE;
}

2.首先调用socket(int af, int type, int protocol)创建用于绑定的socket变量,再调用bind(SOCKET s, const struct sockadddr FAR* name, int namelen)进行IP及端口与socket变量的绑定,开始监听,监听后可以使用WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);进行相应事件的处理。

s为绑定的socket变量,hWnd为接受消息的窗口句柄,wMsg是传给窗口的消息,lEvent 为被注册的网络事件。lEvent  = FD_ACCEPT表示当有连接请求时发送消息给消息处理函数进行处理,进而通知服务器去accept请求, lEvent  = FD_READ时表示套接字s上有数据时发送消息通知消息处理函数进行处理,详见下面的程序。

void CData_TransmitDlg::OnBnClickedListenButton()
{
// TODO: 在此添加控件通知处理程序代码
CString strPort = _T("");
GetDlgItemText(IDC_PORT_EDIT, strPort);
if(strPort.IsEmpty())
{
AfxMessageBox(_T("输入端口号"));
return ;
}
closesocket(m_Listen);
//创建套接字
m_Listen = socket(AF_INET, SOCK_STREAM,0);//SOCK_STREAM表示TCP协议
if(m_Listen == INVALID_SOCKET)
{
AfxMessageBox(_T("创建套接字失败"));
return ;
}
WSAAsyncSelect(m_Listen, m_hWnd, WM_SOCKET, FD_ACCEPT|FD_READ);/*用于告知服务器已经检测连接要去接受连接,也可以在监听成功后采用线程去监测数据*/
//获取本地计算机的IP
CString strAddr = _T("");

GetDlgItemText(IDC_PORT_EDIT, strPort);
CString strHostname = _T("");
gethostname(strHostname.GetBuffer(MAX_PATH), MAX_PATH);
strHostname.ReleaseBuffer();
struct hostent* pHostInfo = gethostbyname(strHostname);
strAddr = inet_ntoa(*(in_addr*)pHostInfo->h_addr_list[0]);
//套接字地址
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr(strAddr);
addr.sin_port = htons(atoi(strPort));
//绑定套接字

        int nRes = bind(m_Listen, (sockaddr*)&addr, sizeof(addr)) ;
if(nRes ==SOCKET_ERROR)
{
MessageBox("绑定套接字失败");
return ;
}
//开始监听
if(listen(m_Listen, 5) == 0)
{
MessageBox("监听连接成功");
}
else 
{
MessageBox("监听连接失败");

}

3.自定义的WM_SOCKET消息处理函数,完成接受连接和处理数据:

//WM_SOCKET消息处理函数
LRESULT  CData_TransmitDlg::OnSocket(WPARAM wParam,LPARAM lParam)
{
switch(lParam)
{
case FD_ACCEPT:
closesocket(m_Accept);
//接受连接
m_Accept = accept(m_Listen , NULL , NULL);//接受连接
if(m_Accept != INVALID_SOCKET)
{
MessageBox("接受连接成功");
}
else
{
MessageBox("接受连接失败");
}

break;
case FD_READ: //有客户端发来的数据


TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
int nRes = recv(m_Accept, pBuffer, 1024, 0);
if(nRes != SOCKET_ERROR)
{
SetDlgItemText(IDC_DATA_RECV_EDIT, pBuffer);
delete[] pBuffer;
break;
}
}

return 1;
}

4.服务端发送数据:

void CData_TransmitDlg::OnBnClickedSendButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str = "";
GetDlgItemText(IDC_DATA_SEND_EDIT, str);
int nRes = send(m_Accept, str, 1024, 0);
if( nRes == SOCKET_ERROR)
{
MessageBox(_T("数据发送失败"));
}
}

客户端通信流程:

1.向服务器发出连接请求:

void CClientDlg::OnBnClickedConnectButton()
{
// TODO: 在此添加控件通知处理程序代码
CString SrvrIp;
GetDlgItemText(IDC_IP_EDIT,SrvrIp);
if(SrvrIp.IsEmpty())
{
MessageBox(_T("输入服务器IP地址"));
return ;
}
CString strPort = _T("");
GetDlgItemText(IDC_PORT_EDIT, strPort);
if(SrvrIp.IsEmpty())
{
MessageBox(_T("输入服务器端口号"));
return ;
}
//关闭套接字
closesocket(m_Connect);
// 创建套接字
m_Connect = socket(AF_INET ,SOCK_STREAM, 0);
WSAAsyncSelect(m_Connect, m_hWnd, WM_RECV, FD_READ);
if(m_Connect == INVALID_SOCKET)
{
MessageBox(_T("创建套接字失败"));
return ;
}
//套接字地址
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr(SrvrIp);
addr.sin_port = htons(atoi(strPort));
m_SrvrAddr = addr;
//建立连接
if(0 == connect(m_Connect,(sockaddr*)&addr, sizeof(addr)))

MessageBox(_T("建立连接成功"));
/*
m_ThreadParam.hWnd = m_hWnd;
m_ThreadParam.str = _T("");
m_ThreadParam.socket = m_Connect;
AfxBeginThread((AFX_THREADPROC)RecvPro, &m_ThreadParam);
*/
}
else
{
MessageBox(_T("建立连接失败"));
}
}

2.处理来自服务器的数据,通过网络事件FD_READ完成,当m_Connect上有来自服务器的数据就会发送消息

// 接受数据处理
LRESULT CClientDlg::OnRecvData(WPARAM wParam,LPARAM lParam)
{
switch(lParam)
{
case FD_READ:


TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
int nRes = recv(m_Connect, pBuffer, 1024, 0);
if(nRes != SOCKET_ERROR)
{
SetDlgItemText(IDC_DRECEV_EDIT, pBuffer);
delete[] pBuffer;
break;
}
//
/*CString str = m_ThreadParam.str;
SetDlgItemText(IDC_DRECEV_EDIT, str);*/

}
return 0;
 }

3.客户端进行数据发送:

void CClientDlg::OnBnClickedSendButton()
{
// TODO: 在此添加控件通知处理程序代码

CString str = "";
GetDlgItemText(IDC_DSEND_EDIT, str);
int nRes = send(m_Connect, str, 1024, 0);
if( nRes == SOCKET_ERROR)
{
MessageBox(_T("数据发送失败"));
}
}

4.补充:

如果不使用网络事件进行消息处理,也可以采用线程进行监听数据

//监测来自服务器的数据,线程函数如下
DWORD CClientDlg::RecvPro(LPVOID lParam)
{
TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
THREAD_PARAM* pThreadParam = ((THREAD_PARAM*)lParam);
HWND hwnd = pThreadParam->hWnd;
while(TRUE)
{
int nRes = recv(pThreadParam->socket, pBuffer, 1024, 0 );
if(nRes != SOCKET_ERROR)
{
pThreadParam->str = pBuffer;
::PostMessage(hwnd, WM_RECV, 0 ,0);
}
}
delete[] pBuffer;
return 0;
 }
 

//自定义消息WM_RECV处理结果函数

// 接受数据处理
LRESULT CClientDlg::OnRecvData(WPARAM wParam,LPARAM lParam)
{
CString str = m_ThreadParam.str;
SetDlgItemText(IDC_DRECEV_EDIT, str);

}
return 0;
 }

5.通信结果如下:


UDP通信测试:

客户机代码:

1.接受方面流程:


//初始化
BOOL CServerDlg::OnInitDialog()
{
/*.....
*/
// TODO: 在此添加额外的初始化代码
m_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(m_socket==INVALID_SOCKET)
{
MessageBox("创建失败!");
return FALSE;
}
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.103");       
addr.sin_port = htons(8888);//接受端口,用于接收数据
int Result = bind(m_socket,(sockaddr*)&addr, sizeof(SOCKADDR));//绑定
if(Result == SOCKET_ERROR)
{
MessageBox("套节字帮定失败!");
closesocket(m_socket);
return FALSE;
}
m_Addr1 = addr;
addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.103");
addr.sin_port = htons(5601);//发送端口
m_Addr2 = addr; 
PARAM_THREAD* p = new PARAM_THREAD;
p->socket = m_socket;
p->str = "";
p->hWnd = m_hWnd;
HANDLE handle=::CreateThread(NULL,0,RecvPro,(LPVOID)p,0,NULL);//建立监听线程
CloseHandle(handle);
return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

//线程函数,用于监听消息
DWORD CServerDlg::RecvPro(LPVOID lParam)
{
PARAM_THREAD* pParamThread = (PARAM_THREAD*)lParam;
HWND hwnd = pParamThread->hWnd;
SOCKET socket = pParamThread->socket;
TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
SOCKADDR_IN CliAddr;
int addr_len = sizeof(CliAddr);
while(TRUE)
{
int nRes = recvfrom(socket, pBuffer, 1024,0,(sockaddr*)&CliAddr,&addr_len);

if(nRes == SOCKET_ERROR)
{
AfxMessageBox(_T("接受数据错误"));
break;
}
pBuffer[strlen(pBuffer)+1] = '\0';
::PostMessage(hwnd, WM_RECV,(WPARAM)&CliAddr,(LPARAM)pBuffer);
}
delete[] pBuffer;
return 0;
 }
//消息处理
LRESULT CServerDlg::OnRecv(WPARAM wParam, LPARAM lParam)
{
CString str = (char*)lParam;
SetDlgItemText(IDC_RECV_EDIT, str);
return 0;
}

2.发送数据流程:

void CServerDlg::OnBnClickedSendButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str = "";
GetDlgItemText(IDC_SEND_EDIT, str);
int addr_len = sizeof(struct sockaddr_in);
sockaddr_in addr = m_Addr2;
SOCKET sdocket = socket(AF_INET, SOCK_DGRAM, 0);
int nRes = sendto(sdocket ,str, 1024, 0,(sockaddr*)&m_Addr2,addr_len);
if(nRes == SOCKET_ERROR)
{
MessageBox("数据未发送");
}
}

3. 通信结果如下:


© 著作权归作者所有

共有 人打赏支持
小菜先生
粉丝 0
博文 2
码字总数 3261
作品 0
杭州
私信 提问
《TCP/IP详解 卷1:协议》系列分享专栏

《TCP/IP详解卷1:协议》是一本详细的TCP/IP协议指南,计算机网络历久不衰的经典著作之一。 作者理论联系实际,使读者可以轻松掌握TCP/IP的知识。阅读对象为计算机专业学生、教师以及研究网络...

开元中国2015
2018/11/15
0
0
136-基于 UDP 协议的通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/q1007729991/article/details/68947623 前面我们所实现的通信程序,是基于 TCP 协议的,有连接的方式。也就是...

--Allen--
2017/04/01
0
0
基于tcp和udp的socket实现

本文介绍如何用Java实现Socket编程。首先介绍Java针对Socket编程提供的类,以及它们之间的关系。然后分别针对TCP和UDP两种传输层协议实现Socket编程。 1 Java中的Socket编程接口介绍 Java为S...

chjuaner
2017/11/07
0
0
[转]TCP/IP传输层,你懂多少?

你所不知道的传输层 题记:23页的文档上,满满当当的写满了笔记,纸质的东西,始终害怕丢失,还是选择把它总结到博客上来。 PS.老规矩,列出可能遇到的20个问题,如果您是都能回答的高手,请...

穿越星辰
2011/04/22
0
2
网络编程懒人入门(五):快速理解为什么说UDP有时比TCP更有优势

本文观点仅作参考,请根据自已系统的应用场景合理地选择数据传输层协议即可,无需盲目崇拜大牛言论。 1、前言 对于即时通讯开者新手来说,在开始着手编写IM或消息推送系统的代码前,最头疼的...

JackJiang2011
2017/12/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

lua中.和:的区别

lua中.和:都可以用于方法的声明和调用。和table配合使用。 :和.最大的不同点,就是:会把调用者自身,传入到函数中。 如下代码: c = {a=1,b=2}function c:foo1()print(self.a,self.b)...

乐_然
13分钟前
0
0
CentOS7 NTP客户端和服务器安装和使用

本文测试环境如下: [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) [root@localhost ~]# [root@localhost ~]# yum install ntp#等待安装完成......

白豆腐徐长卿
23分钟前
6
0
Tensorflow源码解析3 -- TensorFlow核心对象 - Graph

1 Graph概述 计算图Graph是TensorFlow的核心对象,TensorFlow的运行流程基本都是围绕它进行的。包括图的构建、传递、剪枝、按worker分裂、按设备二次分裂、执行、注销等。因此理解计算图Gra...

阿里云官方博客
28分钟前
2
0
网站加载不了 图片

公司测试环境,某网站图片加载不了 nginx 配置 server { listen 80;listen 443 ssl;charset utf-8;root /var/www/html/ad-server; index index.html index.htm...

Linux_Anna
30分钟前
1
0
亿级曝光品牌视频的幕后设定

本文由云+社区发表 作者:腾讯ISUX 项目背景 2019年春节期间,QQ红包运营活动进行了全新改版,将卡券福利、现金奖励打包成福袋形式,并通过年俗小游戏及共享福袋的玩法吸引更多用户参与。在点...

腾讯云加社区
33分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部