1.字节序的概念和转换
小端格式: 低位字节数据存储在低地址
大端格式: 高位字节数据存储在低地址
在主机上时为小端存储,在网络上时为大端,所以接收到数据时,要转为小端口
如下图:
#include <arpa/inet.h>
发送者调用的函数:
uint32_t htonl(uint32_t hostlong); //转ip 将32位的主机字节序转换为 网络字符节
uint16_t htons(uint16_t hostshort); //转端口 将16位的主机字节序转换为 网络字符节
接收者者调用的函数:
uint32_t ntohl(uint32_t netlong); //转ip 将32位的网络字节序转换为 主机字符节
uint16_t ntohs(uint16_t netshort); //转端口 将16位的网络字节序转换为 主机字符节
2.IP地址转换
#include <arpa/inet.h>
点分十进制 转为 32位无符号整数
转换函数:
**int inet_pton(int af , const char src , void dst);
参数:
af : 转换的协议
AF_INET (IPv4)
AF_INET6 (IPv6)
src : 点分十进制数串的首元素地址
dst : 4字节的IP地址
返回值: 成功 1 失败 -1
32位无符号整数 转为 点分十进制
**const char *inet_ntop(int af , const void src , char dst, socklen_t size);
参数:
af : 转换的协议, 如 AF_INET (IPv4) AF_INET6 (IPv6)
src :4字节的IP地址的起始地址
dst :存放点分十进制数串的起始地址
size:点分十进制数串的最大长度
#define INET_ADDRSTRLEN 16 //for ipv4
#define INET6_ADDRSTRLEN 46 //for ipv6
返回值: 成功则返回字符串的首地址; 失败则返回NULL
UDP编程
发送方:
// 创建socket
// 创建 地址结构体 sockaddr_in
// 准备 数据
// 发送 sendto()
ssize_t sendto(int sockfd,const void * buf,size_t nbytes, int flags,const struct sockaddr *to, socklen_t addrlen)
参数:
sockfd:套接字
buf: 发送数据缓冲区
nbytes: 发送数据缓冲区的大小
flags:一般为 0
to:指向目的主机地址结构体的指针
addrlen:to 所指向内容的长度
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main(){
int sock_fd = socket(AF_INET,SOCK_DGRAM,0); // 创建socket
// 创建 地址结构体 sockaddr_in
struct sockaddr_in dstaddr;
dstaddr.sin_family=AF_INET;
dstaddr.sin_addr.s_addr =inet_addr("192.168.74.1");
dstaddr.sin_port = htons(8000);
//数据
char data[]="helloudp\n";
//将sockaddr_in 转换为sockaddr 发送数据 sendto()
ssize_t len =sendto(sock_fd,data,sizeof(data),0,(struct sockaddr *)&dstaddr,sizeof(dstaddr));
close(sock_fd);
return 0;
}
接收方:
// 创建socket
// 创建 地址结构体 sockaddr_in
// 让定义的套接字固定绑定 IP 和 port
while(1){
// 准备 数据
// 接收 recvfrom ()
}
**ssize_t recvfrom(int sockfd,void \*buf, size_t nbytes,int flags,struct sockaddr \*from,socklen_t \*addrlen);**
参数:
sockfd: 套接字
buf:接收数据缓冲区
nbytes: 接收数据缓冲区的大小
flags: 套接字标志(常为 0)
from: 源地址结构体指针,用来保存数据的来源
addrlen: from 所指内容的长度
注意:通过 from 和 addrlen 参数存放数据来源信息 ,from 和 addrlen 可以为 NULL, 表示不保存数据来源。
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
int main(){
//定义一个套接字;
int sfd=socket(AF_INET,SOCK_DGRAM,0);
//定义IPv4 的地址结构;
struct sockaddr_in localaddr;
localaddr.sin_family=AF_INET; //指明 IPv4
localaddr.sin_port=htons(8000);//指明端口号,转换为大端口; ???
localaddr.sin_addr.s_addr =htonl(INADDR_ANY);//表示任意的本地IP地址,转换为
//让定义的套接字固定绑定 IP 和 port
bind(sfd,(struct sockaddr *)&localaddr,sizeof(localaddr));
//持续监听-----
while(1){
char buf[128];
struct sockaddr_in fromaddr; //存储 发送者的地址和端口
socklen_t slen;
//接收数据
ssize_t len = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&fromaddr,&slen);
if(len>0){
buf[len]=0;
//接收到数据
char ip[INET_ADDRSTRLEN];
//发送方的IP 转换为点分十进制,同时自动转换为小端口格式
inet_ntop(AF_INET,&fromaddr.sin_addr.s_addr,ip,INET_ADDRSTRLEN);
uint16_t port =ntohs(fromaddr.sin_port);//将发送方的端口转换为小格式
printf("%s:%d->%s\n",ip,port,buf);
if(strncmp(buf,"bye",3)==0) break;
}
}
close(sfd);
return 0;
}