嵌入式Linux网络编程 之 简单的UDP网络编程

2019-07-12 15:14发布

关于用户数据报文协议(UDP) UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。而且UDP提供的是不可靠的非连接型的传输层服务,允许在源站点和目的站点之间传送数据,而不必在传送数据之间建立连接,进而开销比较低,主要用于那些不要求TCP协议的非连接型应用程序。例如:网络管理,视频点播、网络会议...
简单的数据报通信的框图

常用的API:(不完善) 函数名称 socket() 功能,创建一个套接字 原型:int socket(int domain, int type, int protocol) 所属头文件: 成功:返回文件套接字描述符 失败:返回-1 参数说明: domain:网络协议域 , 如AF_INET-- IPv4域 AF_INET6--IPv6域 type : 套接字类型 如SOCK_DGRAM:固定长度的,无连接的,不可靠的报文传递,默认为UDP SOCK_STREAM:有序的,可靠的,双向的,面向连接的字节流,默认为TCP SOCK_RAW:IP协议的数据报接口 protocol: 通常为0 ,表示为给定的域和套接字类型选择一个特定的协议
函数名称bind 功能:关联地址和套接字 原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t len); 所属头文件: 成功:返回0 失败:返回-1 参数说明: sockfd : 绑定的套接字 addr: 绑定的地址,对于因特网域,若指定地址内的IP为INADDR_ANY,则套接字断点可以被绑定到所有的系统网络接口上,这意味着可以接受这个系统所安装的任何一个网卡的数据包 len : 绑定地址的大小。

函数名称:sendto() 功能 : 发送数字,面对无连接的套接字 原型:ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen) 所属头文件: 成功:返回发送的字节数 失败:返回-1 参数说明: sockfd : 套接字 buf :需发送的数据 nbytes: 需发送的数据长度 flags:一般为0 destaddr:目的地址(务必转换类型) destlen:地址长度
函数名称:recvfrom() 功能 : 接收数据,可以获取数据发送者的源地址 原型 : ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen) 所属头文件: 成功:返回接受的字节数 ,无可用数据则返回0 失败:返回-1 参数说明: sockfd : 套接字 buf :接收的数据 nbytes: 接收的数据长度 flags:一般为0 addr :发送数据者的套接字端点地址 addrlen:包含了addr所指向的套接字自缓冲区的字节长度
函数名称colse() 原型: int close(int fd) 功能 : 关闭一个文件,关闭一个套接字 头文件: 成功:0 失败:-1
函数名称:shutdown() 原型:int shutdown(int sockfd, int how) 功能 : 禁止一个套接字的IO 头文件: 成功:0 失败:-1 参数说明: sockfd : 套接字 how : SHUT_RD : 将无法从套接字中读取数据 SHUT_WR:关闭写端,将无法使用套接字发送数据 SHUT_RDWR:关闭读写端,既无法读,又无法发送

服务器收发基本流程: 1. 创建套接字 2. 初始化套接字(把相应字段转换成网络字节序后填入) 3. 绑定套接字到一个具体的地址端口 4. 收发处理        4.1 循环收发数据报        4.2 打印收发的信息 
代码如下: #include #include #include #include #include #include #include #define SERVER_PORT 8880 #define BUFFER 1024 void udp_respon(int sockfd); int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in addr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); bzero(&addr, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT);//注意字节序的转换 addr.sin_addr.s_addr = htonl(INADDR_ANY);//设置IP为INADDR_ANY,这可以绑定套接字端口到任意的本地地址上 if(bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))<0) { printf("Bind Error! "); exit(1); } udp_respon(sockfd); close(sockfd); return 0; } void udp_respon(int sockfd) { struct sockaddr_in addr;//用于接收源端口信息,即客户端的信息 unsigned int addrlen = 16, n; char msg[BUFFER]; //用于接收数据的缓冲区 char echo[] = "[server ehco]"; while(1) { if(n = recvfrom(sockfd, msg, BUFFER, 0, (struct sockaddr*)&addr, &addrlen) < 0) { printf("Recive Error!"); exit(1); } fprintf(stdout, "I have received: %s", msg); strcat(msg, echo); printf("msg = %s ", msg); printf("client port: %d ", ntohs(addr.sin_port)); sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&addr, addrlen); } }



客户端收发基本流程: 1. 判断使用客户端是否合理,及用法 2. 创建服务端套接字(从客户端中提取IP和端口号)和客户端套接字(由客户端代码指定),注意格式的转换 3. 绑定客户端套接字 4.接收处理     4.1 提示输入信息,打印发送数据     4.2 接收服务器回传的数据
代码如下: #include #include #include #include #include #include #include #define BUFFER 1024 void udp_respon(int sockfd, const struct sockaddr_in *addr, int len) { char buffer[BUFFER]; int n; while(1) { fgets(buffer, BUFFER, stdin); sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr*)addr, len); bzero(buffer, BUFFER); n = recvfrom(sockfd, buffer, BUFFER, 0, NULL, NULL); buffer[n] = 0; fprintf(stdout, "I have received: %s", buffer); } } int main(int argc, char *argv[]) { int sockfd, port; struct sockaddr_in s_addr;//服务端地址结构 struct sockaddr_in c_addr;//客户端地址结构 if(argc != 3) { fprintf(stderr, "usage: %s IP Port", argv[0]); exit(1); } if((port = atoi(argv[2])) < 0) //注意此处的优先级 { printf("The port is error "); exit(1); } sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd<0) { fprintf(stderr,"Socket Error:%s ",strerror(errno)); exit(1); } bzero(&s_addr, sizeof(struct sockaddr_in)); s_addr.sin_family = AF_INET; s_addr.sin_port = htons(port); if(inet_pton(AF_INET, argv[1], &s_addr.sin_addr) < 0 ) { fprintf(stderr, "Ip errpr %s ", strerror(errno)); exit(1); } /*if(inet_aton(argv[1], &s_addr.sin_addr) < 0) { fprintf(stderr, "Ip error:%s ", strerror(errno)); exit(1); }*/ bzero(&c_addr, sizeof(struct sockaddr_in)); c_addr.sin_family = AF_INET; c_addr.sin_port = htons(8848); c_addr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(sockfd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) < 0) { fprintf(stderr, "Bind Error: %s ", strerror(errno)); exit(1); } udp_respon(sockfd, &s_addr, sizeof(struct sockaddr_in)); close(sockfd); }


通信演示: 服务端:
客户端:
注:注意赋值符号的优先级比较低。 如: if(a = fuc(b) < c)... 函数结果先与c作比较,在赋值给a. if( ( a = fuc(b) )