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

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

简单的数据报通信的框图


常用的API:(不完善)
函数名称 socket()
功能,创建一个套接字
原型:int socket(int domain, int type, int protocol)
所属头文件:<sys/socket.h>
成功:返回文件套接字描述符   失败:返回-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);
所属头文件:<sys/socket.h>
成功:返回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)
所属头文件:<sys/socket.h>
成功:返回发送的字节数  失败:返回-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)
所属头文件:<sys/socket.h>
成功:返回接受的字节数 ,无可用数据则返回0 失败:返回-1
参数说明:
sockfd : 套接字
buf :接收的数据
nbytes: 接收的数据长度
flags:一般为0
addr :发送数据者的套接字端点地址
addrlen:包含了addr所指向的套接字自缓冲区的字节长度

函数名称colse()
原型: int close(int fd)
功能 : 关闭一个文件,关闭一个套接字
头文件:<unistd.h>
成功:0 失败:-1

函数名称:shutdown()
原型:int shutdown(int sockfd, int how)
功能 :  禁止一个套接字的IO
头文件:<sys/socket.h>
成功:0 失败:-1
参数说明:
sockfd : 套接字
how : 
    SHUT_RD : 将无法从套接字中读取数据
    SHUT_WR:关闭写端,将无法使用套接字发送数据
    SHUT_RDWR:关闭读写端,既无法读,又无法发送


服务器收发基本流程:
1. 创建套接字
2. 初始化套接字(把相应字段转换成网络字节序后填入)
3. 绑定套接字到一个具体的地址端口
4. 收发处理
       4.1 循环收发数据报
       4.2 打印收发的信息 

代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>

#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!\n");
  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\n", msg);
   
   printf("client port: %d\n", ntohs(addr.sin_port));
   
   sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&addr, addrlen);
 }
}




客户端收发基本流程:
1. 判断使用客户端是否合理,及用法
2. 创建服务端套接字(从客户端中提取IP和端口号)和客户端套接字(由客户端代码指定),注意格式的转换
3. 绑定客户端套接字
4.接收处理
    4.1 提示输入信息,打印发送数据
    4.2 接收服务器回传的数据

代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#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\n");
  exit(1);
 }
 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 
 if(sockfd<0)
 {
        fprintf(stderr,"Socket  Error:%s\n",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\n", strerror(errno));
  exit(1);
 }
 
 /*if(inet_aton(argv[1], &s_addr.sin_addr) < 0)
 {
  fprintf(stderr, "Ip error:%s\n", 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\n", 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) ) <c )..与上述表达式有很大不同

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值