MFC的UDP编程实现[通俗易懂]

MFC的UDP编程实现[通俗易懂]1、编程原理UDP是面向非连接的通信协议,比TCP协议简单很多。无论是服务器端还是客户端,其通信过程概括为:创建套接字(socket)–>绑定(bind)–>发送send(或接收recv)–>关闭套接字(closesocket) 2、特殊地址:在实际通信网络中,我们几乎不会用到“0.0.0.0″和“127.0.0.1”这样的IP地址。但是在一台计算机上,特别用于某些测试用


Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

1、编程原理

UDP是面向非连接的通信协议,比TCP协议简单很多。无论是服务器端还是客户端,其通信过程概括为:

创建套接字(socket)–>绑定(bind)–>发送send(或接收recv)–>关闭套接字(closesocket)

 

2、特殊地址:

在实际通信网络中,我们几乎不会用到“0.0.0.0″和“127.0.0.1”这样的IP地址。但是在一台计算机上,特别用于某些测试用途时,这类地址就有用武之地了。

(1)环回地址:127.0.0.1,该地址可用于本地计算机测试接收功能,即本地计算机绑定一IP地址(如192.168.1.2)时,可向环回地址发送信息M,则本地计算机可收到“反馈”回来的同样信息M(具有服务端性质)

(2)全零网络地址:0.0.0.0,可作为源地址,表示整个网络,即“任意地址”

3、重要函数

(1)创建套接字函数socket()

函数原型:SOCKET PASCAL FAR socket( int af, int type, int protocol);

返回值说明:成功返回套接字,失败返回INVALID_SOCKET;

创建流套接字(TCP)时,如:m_socket = socket(AF_INET,SOCK_STREAM,0)

创建数据报套接字(UDP),如:m_socket = socket(AF_INET,SOCK_DGRAM,0)

在成功创建套接字之后,需要填充sockaddr_in结构体作为网络函数参数:

struct sockaddr_in

   {

      shortint sin_family;//地址协议

     unsigned short int sin_port;//端口号

     struct in_addr sin_addr;//IP地址

     unsigned char sin_zero[8];// sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

    }

如在VS2010中,有:

//SOCKADDR_INaddrSock;//SOCKADDR_IN为sockadd_in的宏定义,此变量在头文件中定义

((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS2))->GetAddress(sourceIP);//获取控件上IP地址

addrSock.sin_family=AF_INET;

addrSock.sin_port=htons(6000);

addrSock.sin_addr.S_un.S_addr=htonl(sourceIP);//sourceIP表示运行该程序的主机IP

 

(2)绑定函数bind()

函数原型:int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR *name,int namelen)

返回值说明:绑定成功,返回0值,否则返回-1(SOCKET_ERROR)

如:

int retval;

retval = bind(m_socket,(SOCKADDR*)&addrSock, sizeof(SOCKADDR));//SOCKADDR是sockaddr的宏定义

*(3)创建线程函数CreateThread()

创建线程后,立即开启,调度线程函数:

RECVPARAM *pRecvParam=newRECVPARAM;

pRecvParam->sock=m_socket;

pRecvParam->hwnd=m_hWnd;

HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);//RecvProc为线程函数

CloseHandle(hThread);

 

//线程函数

DWORD WINAPICMyChatDlg::RecvProc(LPVOIDlpParameter)

{

//lpParmeter为创建线程是所提交的函数参数

    SOCKETsock=((RECVPARAM*)lpParameter)->sock;

    HWNDhwnd=((RECVPARAM*)lpParameter)->hwnd;

    deletelpParameter; //释放对象

SOCKADDR_IN addrFrom;

    int len=sizeof(SOCKADDR);

    char recvBuf[200];

    char tempBuf[300];

    int retval;

    while(TRUE)   //创建线程的必要性

    {

    retval=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);//获取套接字接收内容

        if(SOCKET_ERROR==retval)

            break;

        sprintf(tempBuf,”%s说: %s”,inet_ntoa(addrFrom.sin_addr),recvBuf);

 

        ::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);//提交消息,触发消息响应

    }

    return 0;

}

分析:

struct RECVPARAM

{

    SOCKET sock;

    HWND hwnd;

};//在头文件中定义该结构体

 

线程的创建是通过函数CreateThread来实现的,调用成功返回句柄和一个id。

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,  //线程的安全属性,NULL为缺省值

DWORD dwStackSize,                        //线程堆栈的大小,0为系统缺省值

LPTHREAD_START_ROUTINE lpStartAddress,  //线程函数的起始地址可为线程函数名

LPVOID lpParameter,                      //传递给线程函数的参数,重要!

DWORD dwCreationFlags,              //线程创建后是否立即启动,0表示立即启动

LPDWORD lpThreadId                     //线程的ID号

);

(4)获取接收信息的recvfrom函数(经socket接收数据):

函数原型:ssize_trecvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr*from,socket_t *fromlen);

参数含义详见:http://baike.baidu.com/view/1744189.htm

功能描述:该函数接收来自套接字的数据,数据存到缓冲区,并从sockaddr中可读取到相关网络参数(如接收数据的源地址等)

 

(5)发送函数函数sendto()

函数原型:intPASCAL FAR sendto (

IN SOCKET s,

IN const char FAR *buf,

IN int len,

IN int flags,

IN const structsockaddr FAR *to,

IN int tolen);

如:sendto(m_socket,strSend,strSend.GetLength()+1,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR));

4、关键点:

(1)UDP实现过程简单,关键是了解每个过程所需要函数及其使用方法

(2)为UDP通信创建线程,是设计更加合理

(3)套接字创建之后很重要的一步是填充sockaddr_in,绑定的成功与否与该结构体具有紧密的关系。

(4)如果是基于人机交互的实现模式,UDP通信之前的工作可以分成几个模块,而这些模块,注意要共用一个套接字(如在类中定义一个SOCKET变量)。如果有默认式的UDP通信模式,可以将UDP通信之前的工作放在一起,即定义一个initial函数,将这些过程全放进去即可。

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/196203.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • Linux文本处理神器awk实战案例

    Linux文本处理神器awk实战案例1.什么是awk2.打印不同列的内容3.打印行号和列号4.修改输入和输出分隔符5.输入多个文件6.修改某一列的值7.条件筛选后打印8.运算9.正则表达式

    2022年7月11日
  • 怎么获取枚举的值_枚举是值类型吗

    怎么获取枚举的值_枚举是值类型吗最近在做一个学校的系统,其中用到一些枚举,可是在显示下拉列表时要绑定枚举的描述及其枚举值时就只一个一个的默认设死,这样不灵活。有没有其快捷方法?搜了下百度很多相关资料有了些许眉目,代码如下1.首先定

    2022年8月3日
  • Drupal 安装过程「建议收藏」

    Drupal 安装过程「建议收藏」php.ini文件https://drupal.stackexchange.com/questions/164172/problem-installing-in-local-the-translation-server-is-offlinecurl.cainfo=”C:\wamp64\keys\cacert.pem”转载于:https://www.cnblogs.com/A…

    2022年7月20日
  • Pytest(11)allure报告「建议收藏」

    Pytest(11)allure报告「建议收藏」前言allure是一个report框架,支持java的Junit/testng等框架,当然也可以支持python的pytest框架,也可以集成到Jenkins上展示高大上的报告界面。mac环境:

    2022年7月31日
  • CGLIB中BeanCopier源码实现

    CGLIB中BeanCopier源码实现转载:CGLIB中BeanCopier源码实现CGLIB代码包结构1.core2.beans3.reflect4.proxyBeanCopier实现机制1.BeanCopier的使用2.性能分析3.一次调用流程(1)CGLIB做了什么(2)从BeanCopier#create开始(3)KEY_FACTORY的由来(4)AbstractClassGene…

    2022年9月5日
  • 实验室设备管理系统C语言——课程设计实习「建议收藏」

    实验室设备管理系统C语言——课程设计实习「建议收藏」本题目将建立一个实验室设备管理系统,记录实验室所有的实验设备,并及时反响设备的运转状况,使用情况,设备购进以后及时记录设备相关信息,对彻底损坏的设备进行报废,维修的设备信息及有更改信息的设备进行统计,解决人工管理系统没解决的问题以及实现对用户更方便的功能,对由严重问题故障的要及时修理,并记录修理日期、设备名、编号、修理厂家、修理费用、责任人等新设备购入后要立即进行设备登记,包括类别、设备名、编号、型号、规格、单价、数量、购置日期、生产厂家保质期和经办人等信息。随时对现有设备及其修理、报废情况进行统计。

    2022年10月13日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号