Linux-UDP编码接口以及代码实现
Posted 天津 唐秙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux-UDP编码接口以及代码实现相关的知识,希望对你有一定的参考价值。
1. udp的编码接口
1.1 udp的编程流程
1.2 绑定地址信息
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:socked函数返回的套接字描述符,将创建出来的套接字和网卡、端口进行绑定
addr:地址信息结构
sa_family_t sa_family;//地址域信息,占用2个字节,地址信息决定了当前使用什么网络协议(AF_INET, AF_INET6, AF_UNIX)
char sa_data[14];//本质上任何网络程序都不会直接填充这14个字节的字符数组,定义第二个参数,总共占用14个字节。
addrlen:地址信息结构的长度
ipv4:
struct sockaddr_in{…}
路径:vim/usr/include/netinet/in.h
地址域信息,AF_INET,AF_INET6, AF_UNIX:2字节
端口信息:2字节
ipv4版本的ip地址:4字节
通用的数据结构:
ipv4的数据结构:
网络协议栈通过地址域信息来判断传递进来的是哪一个数据结构,判断出来之后,就知道后面的字节如何进行解析。
1.3 发送接口
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
sockfd:套接字描述符
buf:要发送的数据
len:要发送的数据长度
flags:
0:阻塞发送
dest_addr:目标主机的地址信息结构(ip,port)
addrlen:目标主机地址信息结构的长度
返回值:
成功:返回具体发送的字节数量
失败:-1
问题:客户端为什么不推荐绑定地址信息?
本质上是不想让客户端程序端口写死,因为一个端口只能被一个一个进程所绑定,如果客户端在启动的时候绑定的都是一个端口,那么当其他客户端进行绑定的时候,就会绑定失败,而如果客户端没有主动绑定端口,udp客户端在调用sendto的时候,会自动绑定一个空闲的端口,这个端口是操作系统分配的一个空闲的端口。
1.4 接收接口
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字描述符
buf:将数据接收到buf当中
len:buf的最大接收能力
flags:
0阻塞接收
src_addr:这个数据来源的主机的地址信息结构
addrlen:输入输出型参数
输入:在接收之前准备的对端地址信息结构的长度
输出:实际接收回来的地址信息长度
返回值:
接收的字节数量
-1
命令: netstat -anp | grep [端口]
功能: 查看端口信息
close(int sockfd)
1.5 主机字节序和网络字节序之间转换的函数
主机字节序转换成为网络字节序
uint32_t htonl(uint32_t hostlong);//host to network long
uint16_t htons(uint16_t hostshort);//host to network short
网络字节序转换成为主机字节序
uint32_t ntohl(uint32_t netlong);//network to host long
uint16_t ntohs(uint16_t netshort);//network to host short
将字符串的点分十进制的ip地址转换成为uint32_t
in_addr_t inet_addr(const char *cp);
将uint32_t从主机字节序转换成为网络字节序
char *inet_ntoa(struct in_addr in);
1.6 代码
客户端(client)
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
//int socket(int domain, int type, int protocol);
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd < 0)
{
perror("sockfd failed");
return 0;
}
while(1)
{
sleep(1);
char buf[1024] = "i am client";
//通用结构体
struct sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(18989);
dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
// const struct sockaddr *dest_addr, socklen_t addrlen);
int sendto_ret = sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
if(sendto_ret < 0)
{
perror("sendto failed");
return 0;
}
memset(buf, '\\0', sizeof(buf));
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
// struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);
if(recv_size < 0)
{
perror("recvfrom failed");
continue;
}
printf("server say: %s\\n", buf);
}
return 0;
}
服务端(server)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
// int socket(int domain, int type, int protocol);
// damain 当前的地址域使用什么协议
// AF_INET:ipv4
// AF_INET6:ipv6
// AF_UNIX:本地域套接字
// type 创建套接字类型
// SOCK_DGRAM 用户数据报套接字 UDP协议
// SOCK_STREAM 流式套接字 TCP协议
// protocol 使用什么类型的套接字协议
// 0:表示套接字类型默认的协议
// IPPROTO_UDP:17
// IPPROTO_TCP:6
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd < 0)
{
perror("socket failed");
return 0;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
//主机字节序转换成为网络字节序
addr.sin_port = htons(18989);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int bind_ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
if(bind_ret < 0)
{
perror("bind failed");
return 0;
}
while(1)
{
//接收
char buf[1024] = {0};
sockaddr_in addr_recvfrom;
socklen_t addr_recvfromlen = sizeof(addr_recvfrom);
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
// struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvfrom_ret = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&addr_recvfrom, &addr_recvfromlen);
if(recvfrom_ret < 0)
{
continue;
}
printf("i am server, i recv %s from %s:%d\\n", buf, inet_ntoa(addr_recvfrom.sin_addr), ntohs(addr_recvfrom.sin_port));
//发送
memset(buf, '\\0', sizeof(buf));
sprintf(buf, "client: %s : %d, i am server", inet_ntoa(addr_recvfrom.sin_addr), ntohs(addr_recvfrom.sin_port));
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr_recvfrom, sizeof(addr_recvfrom));
}
return 0;
}
以上是关于Linux-UDP编码接口以及代码实现的主要内容,如果未能解决你的问题,请参考以下文章
我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情