Windows 环境下的 Socket 编程 4 - 基于 UDP 的服务器/客户端

news/2024/10/19 11:40:36/

在 TCP 中,套接字之间应该是一对一的关系。若向 10 个客户端提供服务,则除了守门的服务器套接字外,还需要 10 个服务器端套接字。但在 UDP 中,不管是服务器端还是客户端都只需要 1 个套接字

基于 UDP 的数据 I/O 函数

UDP 套接字不会保持连接状态,因此每次传输数据都要添加目标地址信息

  1. sendto 函数
//发送数据函数,成功返回传输的字节数,失败时返回 -1
int sendto(SOCKET s,const char *buf,int len,int flags,const struct sockaddr *to,int tolen);
  • s:套接字
  • buf:指向待传输数据的缓冲区
  • len:待传输数据的长度
  • flags:可选参数,若没有则传递 0
  • to:指向包含目的地址信息的 sockaddr 结构体变量
  • tolen:变量 to 的长度
  1. recvfrom 函数
//接收数据函数,成功时返回接收的字节数,失败时返回 -1
int WSAAPI recvfrom(SOCKET s,char *buf,int len,int flags,struct sockaddr *from,int *fromlen);
  • s:套接字
  • buff:指向保存接收数据的缓冲区
  • len:可接收的最大字节数
  • flags:可选参数,若没有则传入 0
  • from:指向包含发送端地址信息的 sockaddr 结构体变量
  • fromlen:指向保存变量 from 长度的变量

什么时候调用 recvfrom ,当操作系统接收到 UDP 数据, 会自动通知(回调)应用程序吗?
如果 len 很小,对方又发送大数据包,如何知道接收缓冲区的数据大小,以方便多次读取?
如果 len 很大,recvfrom 函数会一直等待吗?

基于 UDP 的回声服务器端/客户端

UDP 不同于 TCP,不存在请求连接和受理过程,因此在某种意义上无法明确区分服务器端和客户端。只是因其提供服务而称为服务器端。
服务器端实现:

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void ErrorHandler(char *message);#define BUF_SIZE 1024
int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServSock, hClntSock;struct sockaddr_in servAddr, clntAddr;int szClntAddr;char message[BUF_SIZE];int strLen, i;if(argc != 2){printf ("Usage: %s <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)ErrorHandler("WSAStartup failed");hServSock = socket(PF_INET, SOCK_DGRAM, 0);if(hServSock == INVALID_SOCKET)ErrorHandler("socket error");memset(&servAddr, 0, sizeof(servAddr));servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = htonl(INADDR_ANY);servAddr.sin_port = htons(atoi(argv[1]));if(bind(hServSock, (struct sockaddr *)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)ErrorHandler("bind socket error");while(1){strLen = recvfrom(hServSock, message, BUF_SIZE, 0, (struct sockaddr *)&clntAddr, &szClntAddr);sendto(hServSock, message, strLen, 0, (struct sockaddr *)&clntAddr, sizeof(clntAddr));}closesocket(hServSock);WSACleanup();return 0;
}void ErrorHandler(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

客户端实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>#define BUF_SIZE    1024
void ErrorHandler(char *message);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hClntSock;struct sockaddr_in servAddr,fromAddr;char message[BUF_SIZE];int strLen;int adr_sz;if(argc != 3){printf ("Usage: %s <IP> <port>\n", argv[0]);exit(1);}if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)ErrorHandler("WSAStartup failed");hClntSock = socket(PF_INET, SOCK_DGRAM, 0);if(hClntSock == INVALID_SOCKET)ErrorHandler("socket error");memset(&servAddr, 0, sizeof(servAddr));servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = inet_addr(argv[1]);servAddr.sin_port = htons(atoi(argv[2]));while(1){fputs("Input message(Q to quit): ",stdout);fgets(message, BUF_SIZE, stdin);if(!strcmp(message, "q\n") ||!strcmp(message, "Q\n"))break;sendto(hClntSock, message, strlen(message), 0, (struct sockaddr *)&servAddr, sizeof(servAddr));strLen = recvfrom(hClntSock, message, BUF_SIZE, 0, (struct sockaddr *)&fromAddr, &adr_sz);message[strLen] = 0;printf("Message from server: %s", message);}closesocket(hClntSock);WSACleanup();return 0;
}void ErrorHandler(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

UDP 套接字存在数据边界

TCP 数据传输不存在边界,这表示数据传输过程中调用 I/O 函数的次数不具有任何意义。但是,UDP 是具有数据边界的协议,传输中调用 I/O 函数的次数非常重要。调用 3 次 sendto 发送的函数要调用 3 次 recvfrom 函数才能接收完。

已连接的 UDP 套接字

使用 sendto 函数,每次都要传入目标地址信息,这在某些场合会低效且繁琐,已连接的 UDP 套接字可以解决这个问题

  1. connect 函数:创建已连接的 UDP 套接字
/*成功返回 0 ,失败返回 SOCKET_ERROR */
int connect(SOCKET s, const struct sockaddr *name, int namelen);

这个函数参数与 TCP 相同,但是结果不同,TCP 调用这个函数会进行 3 次握手,UDP 调用这个函数只是注册目标 IP 和端口信息。
一旦调用 connect 函数后,就不必使用 sendtorecvfrom 函数发送和接收数据,取而代之的是和 TCP 相同的 API 函数:sendrecv


http://www.ppmy.cn/news/655.html

相关文章

操作系统学习笔记(Ⅳ):文件

目录 1 文件管理 1.1 初识文件管理 1.文件属性 2.文件数据组织 3.向上功能 1.2 文件逻辑结构 1.无结构文件 2.有结构文件 3.顺序文件 4.索引文件 5.索引顺序文件 1.3 文件目录 1.文件控制块 2.目录结构 3.索引结点 1.4 文件物理结构 1.连续分配 2.链接分配 …

品质创未来!流辰信息技术公司实力谱新章!

对于一个企业而言&#xff0c;一辈子用心做好一件事就是对社会最大的贡献。自从深耕于低代码开发平台领域以来&#xff0c;流辰信息技术公司便时刻保持着奋斗和创新的研发心态&#xff0c;不仅增强自主创新能力&#xff0c;而且还积极扩大队伍团队和实力&#xff0c;立志为每一…

数据库学习

数据是描述事务的符号记录&#xff0c;包括数字、文字、图像、音频等&#xff0c;以“记录”的形式按统一的格式进行存储&#xff1b;表将不同的记录组织在一起&#xff0c;用来存储具体的数据&#xff1b;数据库是表的集合&#xff0c;是存储数据的仓库&#xff0c;它以一定的…

【Javadoc生成开发文档(Terminal或IDEA中)】

Javadoc生成开发文档一、Javadoc工具介绍二、常用标记三、使用方式四、生成文档的两种方式1.Terminal方式2.IDE方式一、Javadoc工具介绍 大家在查看官网文档的时候&#xff0c;会不会感慨人家的帮助文档写的真有逻辑&#xff0c;层次分明&#xff1f; 不要羡慕&#xff0c;你…

【踩坑】double和BigDecimal的精度问题

【踩坑】double和bigdecimal的精度问题背景debug尝试测试代码结果总结背景 今天生产报了个问题&#xff0c;在申报和预算相同的金额的时候&#xff0c;后台返回超出预算。一开始以为是判断逻辑的问题&#xff0c;找了个数据试了下发现重现不出来。又是改数据又是找前端传参最后…

手撕一个图片色卡提取器,可自定义提取色卡数量!

在一些特殊的业务场景中&#xff0c;我们需要一次性提取一张图片中的色卡信息&#xff0c;并且需要使用十六进制的颜色表示方法进行展示。 今天得空做了一个小工具&#xff0c;用来自定义的提取某一张图片中的色卡信息&#xff0c;需要提取某张图片中的色卡可以自行选择。 实现…

m基于可见光通信系统的RFID接口过程以及ALOHA防碰撞算法的matlab仿真

目录 1.算法描述 2.matlab算法仿真效果 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 射频识别技术(Radio Frequency Identification&#xff0c;RFID)是一种非接触式自动识别技术&#xff0c;与传统的识别方式相比&#xff0c;它无需直接接触、无需光学可视、无需人工干预即…

[附源码]Python计算机毕业设计Django第三方游戏零售平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…