WinSock API网络编程——UDP协议(http://www.impcas.ac.cn/usr/lujun/browse.asp?id=winsock_udp)

2023-05-16

WinSock API网络编程——UDP协议
作者:陆军 Email:ldlujun@163.com 时间:2004-11-14

UDP协议(User Datagram Protocol),即用户数据报协议,是定义用来在互连网络环境中提供包交换的计算机通信的协议。它是Internet上广泛采用的通信协议之一。UDP协议直接位于IP协议的顶层,属于传输层协议,它提供向另一用户程序发送信息的最简便的协议机制。

与TCP协议不同,UDP协议是一个无连接协议,发送端和接收端不建立连接;UDP协议不提供数据传送的保证机制,可以说它是一种不可靠的传输协议;UDP协议也不能确保数据的发送和接收顺序,实际上,这种乱序性很少出现,通常只是在网络非常拥挤的情况下才可能发生。

既然UDP协议有着如此多的缺点,那么它存在的意义何在?其实正是由于UDP协议的这些缺点,才使得它具有许多TCP协议所望尘莫及的优势。TCP协议植入的各种安全保障功能加大了执行过程中的系统开销,使速度受到严重的影响;而UDP不提供信息可靠传递机制,将安全和排序等功能移交给上层应用来完成,极大地提高了执行速度。UDP协议执行速度快,适合视频、音频、文件等大规模数据的网络传输。

尽管UDP协议与TCP协议存在着巨大的差异,但程序设计的基本步骤还是差不多的。为了不再重复我在《WinSock API网络编程——TCP/IP协议》一文中的内容,这里将主要介绍UDP协议与TCP协议在程序设计上的不同之处。如果你是初学者,并且在阅读本文的时候还未阅读上文,那么建议你先看一下。

UDP协议不存在TCP协议中的服务端和客户端之分,相对于TCP协议的C/S模型,UDP协议的通信模型更为对称。在UDP协议网络通信中,根据功能的不同,可以划分为发送端和接收端,但这种划分是一种动态的划分,而不是绝对的,同一个套接字在某一时刻发送数据,那么就是发送端,而在另一时刻接收数据,那么就是接受端。也就是说,同一套接字既可以是发送端也可以是接收端。

另外,前面提到了UDP协议是一个无连接协议,这就是说,我们在编写基于UDP协议的网络通信程序时,不需要监听端口、请求连接、接受连接请求和断开连接。

UDP协议的对称性和无连接特性就决定了实现基于UDP协议的网络通信比实现基于TCP协议的网络通信在程序设计上要简单得多。但是,在实际应用中,由于UDP协议的不可靠性和无序性,往往需要由上层应用来完成安全和排序等功能。

以下将给出利用WinSock API实现基于UDP协议的网络编程的具体步骤和源代码。

(1) 初始化通信端口。可以在程序向导中添加Windows Sockets支持,或者直接添加代码:

#include <afxsock.h>
if (!AfxSocketInit())
{
    AfxMessageBox("Windows 通信端口初始化失败!");
}

(2) 初始化Windows Sockets DLL。目前Winsock有两个版本,版本号分别为1.1和2.2,对应参数为0x101和0x202。

WSADATA wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0)
{
    AfxMessageBox("加载Windows Sockets DLL失败!");
    WSACleanup();
}

(3) 创建流式套接字。请注意socket()函数的第二个参数相对于ICP协议有什么变化。

套接字族:
AF_UNIX:UNIX内部协议族
AF_INET:Iternet协议
AF_NS:XeroxNs协议
AF_IMPLINK:IMP链接层
套接字类型:
SOCK_STREAM:流式套接字
SOCK_DGRAM:数据报套接字
SOCK_RAW:原始套接字
SOCK_SEQPACKET:定序分组套接字

SOCKET m_Socket;
m_Socket = INVALID_SOCKET;
if ((m_Socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
    AfxMessageBox("创建套接字失败!");
}

(4) 服务端绑定端口。端口号范围:1024到65535,低于1024的端口对应着因特网上的一些常见服务。

struct sockaddr {
    u_short sa_family;               // 地址族地址族 address family
    address family char sa_data[14]; // 14字节的协议地址 up to 14 bytes of direct address
};
typedef struct sockaddr SOCKADDR;
typedef struct sockaddr *PSOCKADDR;
typedef struct sockaddr FAR *LPSOCKADDR;

struct sockaddr_in {
    short sin_family;        // 地址族
    u_short sin_port;        // 端口号
    struct in_addr sin_addr; // IP地址
    char sin_zero[8];        // 填充0
};
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr_in *PSOCKADDR_IN;
typedef struct sockaddr_in FAR *LPSOCKADDR_IN;

字节顺序转换函数:
    htons():"Host to Network Short"
    htonl():"Host to Network long"
    ntohs():"Network to Host Short"
    ntohl():"Network to Host Long"

SOCKADDR_IN m_saAddr;
u_short     m_nPort = 20049;                // 端口号
ZeroMemory(&m_saAddr, sizeof(m_saAddr));
m_saAddr.sin_family      = AF_INET;
m_saAddr.sin_port        = htons(m_nPort);  // 如果此值为0,系统将随机选择一个未被使用的端口号
m_saAddr.sin_addr.s_addr = INADDR_ANY;      // 填入本机IP地址
if (bind(m_Socket, (LPSOCKADDR) &m_saAddr, sizeof(m_saAddr)) == SOCKET_ERROR)
{
    AfxMessageBox("绑定端口失败!");
}

(5) 注册网络事件。

网络事件定义:
FD_READ:网络数据包到达
FD_WRITE:发送网络数据
FD_OOB:OOB数据到达
FD_ACCEPT:收到连接请求
FD_CONNECT:已建立连接
FD_CLOSE:断开连接
FD_QOS:服务质量(QoS)发生变化
FD_GROUP_QOS:保留事件
FD_ROUTING_INTERFACE_CHANGE:指定地址的路由接口发生变化
FD_ADDRESS_LIST_CHANGE:本地地址变化

#define WM_NETWORK_EVENT WM_USER + 102
if (WSAAsyncSelect(m_Socket, m_hWnd, WM_NETWORK_EVENT, FD_READ) == SOCKET_ERROR)
{
    AfxMessageBox("注册网络事件失败!");
}

(6) 处理网络事件。

afx_msg LRESULT OnNetworkEvent(WPARAM wParam, LPARAM lParam);
ON_MESSAGE(WM_NETWORK_EVEN, OnNetworkEvent)

LRESULT OnNetworkEvent(WPARAM wParam, LPARAM lParam)
{
    switch (WSAGETSELECTEVENT(lParam))
    {
    case FD_READ:
        // 接收数据
        break;
    }
    return 0L;
}

(7) 读取数据。

BOOL Read(void)
{
    int nBytesRead;
    int nBufferLength;
    int nEnd;
    int nSpaceRemaining;
    char chIncomingDataBuffer[4096];
    SOCKADDR_IN m_saFromAddr;
    int nLenght = sizeof(m_saFromAddr);
    ZeroMemory(&m_saFromAddr, sizeof(SOCKADDR_IN));
    nEnd = 0;
    nBufferLength = sizeof(chIncomingDataBuffer);
    nSpaceRemaining = sizeof(chIncomingDataBuffer);
    nSpaceRemaining -= nEnd;

    nBytesRead = recvfrom(m_Socket, (LPSTR) (chIncomingDataBuffer + nEnd), nSpaceRemaining, 0, (LPSOCKADDR) &m_saFromAddr, &nLenght);
    nEnd += nBytesRead;
    if (nBytesRead == SOCKET_ERROR)
    {
        AfxMessageBox("读取数据出错!")
        return FALSE;
    }
    // IP地址:inet_ntoa(m_saFromAddr.sin_addr);
    // 端口号:ntohs(m_saFromAddr.sin_port);
    chIncomingDataBuffer[nEnd] = '/0';
    if (lstrlen(chIncomingDataBuffer) != 0)
    {
        AfxMessageBox(chIncomingDataBuffer);
    }
    return TRUE;
}

(8) 发送数据。

BOOL Send(CString sIP, u_short nPort, CString sSendData)
{
    DWORD        dwIP;
    SOCKADDR_IN  saAddr;
    if (m_Socket == INVALID_SOCKET)
    {
        AfxMessageBox("套接字不可用!");
        return FALSE;
    }
    if ((dwIP = inet_addr(sIP)) == INADDR_NONE)
    {
        AfxMessageBox("无法获取目标IP!");
        return FALSE;
    }
    saAddr.sin_family      = AF_INET;
    saAddr.sin_port        = htons(nPort);
    saAddr.sin_addr.s_addr = dwIP;
    if (sendto(m_Socket, sSendData, sSendData.GetLength(), 0, (LPSOCKADDR) &saAddr, sizeof(saAddr)) == SOCKET_ERROR)
    {
        AfxMessageBox("发送数据失败!");
        return FALSE;
    }
    return TRUE;
}

(9) 关闭套接字。

if (m_Socket != INVALID_SOCKET)
{
    closesocket(m_Socket);
}
m_Socket = INVALID_SOCKET;
WSACleanup();

UDP协议真正的优势在于它具有TCP协议所不具备的功能,如:广播、多播和穿透NAT等等。这些东西我会在以后的文章中一一讨论,本文到此为止。

希望本文能够对网络编程的初学者有所帮助。由于时间匆忙,文中给出的代码都没有经过调试,难免有错误和不足之处,敬请大家原谅,同时也真心希望您能指出和纠正。

注意:本文版权归原作者所有。如需转载本文,请与我们联系,经原作者和本站同意后方可,同时必须保证文章的完整性,保留原作者名称、原作者联系方式以及文章出处等信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WinSock API网络编程——UDP协议(http://www.impcas.ac.cn/usr/lujun/browse.asp?id=winsock_udp) 的相关文章

  • 跌倒检测和识别3:Android实现跌倒检测(含源码,可实时跌倒检测)

    跌倒检测和识别3 xff1a Android实现跌倒检测 含源码 xff0c 可实时跌倒检测 目录 跌倒检测和识别3 xff1a Android实现跌倒检测 含源码 xff0c 可实时跌倒检测 1 前言 2 跌倒检测数据集说明 3 基于YO
  • 跌倒检测和识别4:C++实现跌倒检测(含源码,可实时跌倒检测)

    跌倒检测和识别4 xff1a C 43 43 实现跌倒检测 含源码 xff0c 可实时跌倒检测 目录 跌倒检测和识别4 xff1a C 43 43 实现跌倒检测 含源码 xff0c 可实时跌倒检测 1 前言 2 跌倒检测模型 xff08 Y
  • 接口测试工具:Postman

    无论是接口调试还是接口测试 xff0c postman都算的上很优秀的工具 xff0c 好多接口测试平台 接口测试工具框架的设计也都能看到postman的影子 xff0c 我们真正了解了这款工具 xff0c 才可以在这个基础上进行自己的设计
  • ROS实验笔记之——move_base_simple/goal

    本博文打算通过节点发布导航的坐标让机器人自动移动到目标点 通过自定义节点来实现导航功能 nbsp 目录 创建仿真环境 编写导航发布者 move base msgs MoveBaseActionGoal Message 实现代码 参考资料 n
  • ROS实验笔记之——自主搭建四旋翼无人机

    最近搭建了一台小的四旋翼无人机 xff0c 本博文记录一下搭建的过程以及一些问题 请问我博客就记录了自己做实验的搭建的飞机有什么问题 xff1f xff1f xff1f 目录 组装 飞行前准备 试飞 组装 首先是一系列的散装原件 到最后搭建
  • ROS实验笔记之——基于l515激光相机的FLVIS与MLMapping

    之前博客 ROS实验笔记之 VINS Mono在l515上的实现 在l515上实现了vins xff0c 博客 ROS实验笔记之 SLAM无人驾驶初入门 配置flvis并跑了对应的kitti数据集 本博文在l515上先实现flvis然后再用
  • Chapter 2. ROS 创建和编译功能包

    1 创建ROS功能包 使用catkin create pkg命令来创建一个新的catkin程序包 首先切换到之前通过创建catkin工作空间教程创建的catkin工作空间中的src目录下 xff1a ros workspace span c
  • linux ulimit命令用法解析

    以下内容转载自 xff1a http www linuxidc com Linux 2012 10 72782 htm Linux对于每个用户 xff0c 系统限制其最大进程数 为提高性能 xff0c 可以根据设备资源情况 xff0c 设置
  • 机体坐标系的角速度分量

    一 角速度分量 机体坐标系的三个角速度分量 xff0c 是机体坐标系相对于地面坐标系的转动角速度在机体坐标系各轴上的分量 其中 xff1a 角速度 xff50 xff0c 与机体轴 xff58 重合一致 xff1b 角速度 xff51 xf
  • 使用Realsense测试aruco_ros包

    01 准备工作 安装realsense ros安装aruco ros span class token builtin class name cd span ur ws src span class token function git s
  • DIY遥控船(一):电调和舵机的驱动[使用STC89C52]

    在动力模型中 xff0c 有两样东西是最基本 最必要的 xff0c 即舵机和无刷电机 舵机提供转动特定角度的功能 xff0c 而无刷电机需要由电调 xff0d xff0d 电子调速器驱动 舵机 舵机又叫伺服电机 xff0c 可以按照输入的指
  • GD32VF103之CRC

    在GD32VF103内部有一个CRC 循环冗余校验计算单元 xff0c 使用它可以对数据的完整性和正确性进行校验 xff0c 比如固件的完整性和正确性校验 通信数据的校验等 它使用固定的32位多项式 xff1a 0x4C11DB7 xff1
  • GD32VF103之GPIO最小配置

    longan nano是Sipeed xff08 矽速科技 xff09 推出的开发板 xff0c 使用兆易创新的gd32vf103cbt6芯片 xff0c 该芯片是基于芯来科技的Nuclei Bumblebee处理器的32位通用微控制器 x
  • Linux控制I2C/SMBus设备

    平台 xff1a 树莓派 bcm2835 Raspberry Pi 3 Model B Rev 1 2 I2C是Philips开发的一种两线通信协议 xff0c 常用于一些对速度要求不高的小型器件上 SMBus是系统管理总线 xff0c 基
  • ArduPilot/APM源码学习笔记(一)

    最近开始学习ArduPilot APM飞控的源码 xff0c 源码托管在github上 源码链接 xff1a https github com diydrones ardupilot 飞控主页 xff1a http ardupilot co
  • GRUB2引导修复

    本来是想把GRUB2装到U盘 xff0c 却不小心把电脑的GRUB搞坏了 原因可能是我执行命令grub install时没有加任何参数 xff0c 由于不知道没有参数怎么执行 xff0c 我赶紧ctrl 43 c终止了安装 xff0c 最后
  • ardupilot的libraries之PID

    在源码的libraries中 xff0c 有两个关于PID的源文件文件夹 xff0c 一个叫AC PID xff0c 另一个是PID AC PID中又细分为AC HELI PID AC P和AC PID xff0c 这里我们只讨论AC PI
  • stm32串口HAL库的DMA发送问题

    本文使用stm32f411ret的串口1的DMA方式发送数据 xff0c 刚开始调试的时候发现串口只能发送一次数据 xff0c 之后就把系统hang住了 通过网上搜资料和不断尝试 xff0c 发现问题是中断回调函数没有写的原因 使用HAL库
  • stm32的HAL库i2c从机实现

    stm32的i2c默认就是slave模式 xff0c 本文基于HAL库实现中断方式的接收和发送 xff0c 首先是初始化gpio和i2c xff0c 代码如下 xff1a I2C HandleTypeDef I2cHandle void H
  • openBLT-系统结构及框架

    openBLT 系统结构及框架 前言1 框架1 1设备层1 2中间件1 2 1 COM1 2 2 BACKDOOR1 2 3 FILE 1 3应用层 前言 openBLT 是开源的小型嵌入式系统bootloader xff0c 目前支持ST

随机推荐