首页 > 网络 > 网络编程 > UDP程序设计
2018
03-07

UDP程序设计

概述

UDP协议是面向无连接的用户数据报协议,在传输数据前不需要先建立连接,目地主机在接收到UDP报文后,也不需要给出任何确认,相比于TCP面向连接的方式,UDP通信具有如下特点:

  • UDP的速度相比TCP稍快些
  • 简单的请求/应答应用程序可以使用UDP
  • 对于海量的数据或是要求可靠的传输时不应该使用UDP
  • 广播和多播必须使用UDP

编程准备

字节序与地址转换

网络通信统一采用大端字节序。在多字节处理时需要考虑字节序。一般同一主机上的进程相互通信时,不用考虑字节序列,而不同的计算机之间通信,则需要转换自己的字节序为网络字节序,可以使用特定的转换函数

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

功能:
完成主机字节序与网络字节序的转换
参数:
要转换的字节数据
返回值:
返回转换的结果

int inet_pton(int family, const char *strptr, void *addrptr);

功能:
将点分十进制数串转换成32位无符号整数
参数:
family:协议族,一般写AF_INET,表示IPv4协议
strptr:点分十进制数串
addrptr:32位无符号整数的地址
返回值:
成功返回1 、 失败返回其它

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

功能:
将32位无符号整数转换成点分十进制数串
参数:
family:协议族
addrptr:32位无符号整数
strptr:点分十进制数串
len strptr:缓存区长度,可以使用以下宏定义

#define INET_ADDRSTRLEN 16 //for ipv4
#define INET6_ADDRSTRLEN 46 //for ipv6

返回值:
成功:则返回字符串的首地址
失败:返回NULL

编程接口

网络编程采用套接字(socket)接口,两个不同的主机网络进程通过套接字描述符进行通信。使用套接字描述符可以解决网络间进行的标识问题以及多重协议的识别问题。套接字是一种文件描述符,代表了一个通信管道的一个端点,类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作。以下是使用UDP进行网络通信编程的架构:

UDP程序设计 - 第1张  | rs232的博客

socket函数

#include <sys/socket.h>
int socket(int family,int type,int protocol);

功能:
创建一个用于网络通信的socket套接字(描述符)
参数:
family:协议族(AF_INET、AF_INET6、PF_PACKET等)
type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)
protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等,一般写0
返回值:
成功返回套接字描述符,失败返回-1
注意:
创建套接字时,系统不会分配端口,需要主动绑定一个端口。创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的。

IPv4套接字地址结构

#include <netinet/in.h>

Internet套接字地址结构sockaddr_in

UDP程序设计 - 第2张  | rs232的博客

通用套接字地址结构

UDP程序设计 - 第3张  | rs232的博客

为了使不同格式地址能被传入套接字函数,地址须强制转换成通用套接字地址结构。所以,在定义源地址和目的地址结构时,选用sockaddr_in,当调用编程函数时,需要要对传入的地址进行强制转换。
例:
struct sockaddr_in my_addr;

bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));

bind函数

int bind(int sockfd, const struct sockaddr *myaddr,socklen_t addrlen);

功能:
将本地协议地址与sockfd绑定
参数:
sockfd: socket套接字
myaddr: 指向特定协议的地址结构指针
addrlen:该地址结构的长度
返回值
成功返回0,失败返回非0.

sendto函数

ssize_t sendto(int sockfd,const void *buf, size_t nbytes,int flags, const struct sockaddr *to, socklen_t addrlen);

功能:
向to结构体指针中指定的ip,发送UDP数据
参数:
sockfd:套接字
buf: 发送数据缓冲区
nbytes: 发送数据缓冲区的大小
flags:一般为0
to: 指向目的主机地址结构体的指针
addrlen:to所指向内容的长度
返回值
成功:成功返回发送数据的字符数,失败返回-1

recvfrom函数

ssize_t recvfrom(int sockfd, void *buf, size_t nbytes,int flags, struct sockaddr *from, socklen_t *addrlen);

功能
接收UDP数据,并将源地址信息保存在from指向的结构中。
参数
sockfd:套接字
buf: 接收数据缓冲区
nbytes:接收数据缓冲区的大小
flags: 套接字标志,一般写0
from: 源地址结构体指针,用来保存数据的来源
addrlen: from所指内容的长度
注意:
通过from和addrlen参数存放数据来源信息,from是一个值-结果参数,既用于传值,也用于保存结果。from和addrlen可以为一起为NULL, 表示不保存数据来源
返回值:
成功:接收到的字符数。
失败: -1

UDP编程示例

接收程序:

#include "common.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
//创建socket
int fd =socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
err_sys("socket error");
//指定本地地址结构
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9999); //指定本机用于绑定的端口号
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本机用于绑定的IP地址为任意IP
//inet_pton(AF_INET, "127.0.0.1", &my_addr.sin_addr);
//绑定
int ret;
ret = bind(fd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(ret != 0)
err_sys("bind error");
printf("receive data...n");
while(1)
{
int recv_len;
char recv_buf[512] = "";
struct sockaddr_in client_addr;
char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16
socklen_t cliaddr_len = sizeof(client_addr);
recv_len = recvfrom(fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("nip:%s ,port:%dn",cli_ip, ntohs(client_addr.sin_port));
printf("data(%d):%sn",recv_len,recv_buf);
sendto(fd, recv_buf, strlen(recv_buf), 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
}
close(fd);
return 0;
}

发送端:

#include "common.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int fd =socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
err_sys("socket error");
char buffer[1024] = {0};
struct sockaddr_in dest_addr;
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(8080); //指定对端端口
unsigned int addr;
inet_pton(AF_INET, "192.168.27.1", &addr);
dest_addr.sin_addr.s_addr = addr;
while(1)
{
fgets(buffer, 1024, stdin);
sendto(fd, buffer, strlen(buffer), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
}
close(fd);
return 0;
}

最后编辑:
作者:rs232
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。