本文列出了在LINUX系统下使用C语言进行UDP收发操作的常用函数和用法
注意:
创建套接字时,系统会分配一个临时端口,默认主动发起服务请求,作为服务器时可修改为被动
发送数据时,不需要用bind绑定端口(当然绑定也可以)
接收数据时,如果不想知道发送者的信息(如:IP地址,端口等),可以不创建发送者结构体,然后recvfrom的后两个参数写NULL
因为网络上的字节序是大端格式(低地址存高字节),所以在发送数据和显示接收的数据时要用htonl/htons和ntohl/ntohs转换(即host to net long/short和net to host long/short)
如果是IP地址,常用inet_pton/inet_ntop
inet_pton(AF_INET, "10.220.4.100", &dst_addr.sin_addr);
char cli_ip[16] = "";
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, sizeof(cli_ip));
当然inet_addr("10.220.4.100")也可以,不过年纪大了,不中用了。
1发送数据
1.1.头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()函数
1.2创建套接字
int sockedfd = 0;
sockedfd = socket(AF_INET,SOCK_DGRAM,0);//创建UDP套接字
if(sockedfd < 0)
{
perror("socket");
exit(-1);
}
1.3发送消息缓冲区
char buf[100]="hello";
1.4填充发送结构体
struct sockaddr_in dst_addr;//目的结构体
bzero(&dst_addr,sizeof(dst_addr));
dst_addr.sin_family = AF_INET;//协议类型
dst_addr.sin_port = htons(8080);//目的端口
inet_pton(AF_INET, "10.220.4.100", &dst_addr.sin_addr); //目的IP地址
1.5发送数据,返回发送的字节数
int len = sendto(sockedfd,buf,strlen(buf),0,(struct sockaddr *)&dst_addr,sizeof(dst_addr));
printf("len = ==%d\n",len);
1.6
关闭套接字
close(sockedfd);
2接收数据
2.1创建套接字
int sockedfd = 0;
sockedfd = socket(AF_INET,SOCK_DGRAM,0);//创建UDP套接字
if(sockedfd < 0)
{
perror("socket");
exit(-1);
}
//填充接收结构体
struct sockaddr_in my_addr;//接收结构体
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = AF_INET;//协议类型
my_addr.sin_port = htons(8080);//接收端口
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//接收IP地址设为任意IP
int err;
//绑定接收端口,便于其它机器访问
err = bind(sockedfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
if(err != 0)
{
perror("bind");
close(sockedfd);
exit(-1);
}
//定义接收缓冲区
char buf[100]="";
//创建发送者结构体,用于储存发送者信息
struct sockaddr_in src_addr;
bzero(&src_addr,sizeof(src_addr));
//发送者结构体的大小
int len=sizeof(src_addr);
//从套接字接收数据
recvfrom(sockedfd,buf,sizeof(buf),0,(struct sockaddr*)&src_addr,&len);
//显示发送端口
printf("port == %d\n",ntohs(src_addr.sin_port));
关闭套接字
close(sockedfd);
下面介绍一下类似于迅雷等下载器的tftp协议
TFTP即简单文本传输协议,工作在应用层,基于UDP,不进行用户认证
只要往69号端口按固定的消息格式发送一条文件请求,另一边的服务器就会把请求的文件发送过来
消息格式:操作码+文件名+0+传输模式+0其中操作码为2字节(1读,2写,3数据,4确认,5错误)
数据包格式为操作码3(2B)+编号(2B)+数据,收到数据包后要发送确认包 操作码4(2B)+块编号
如果数据包小于516字节则说明接收完毕。
具体程序如下:
前面的步骤都一样了
char sendbuf[512]="";
int len = sprintf(sendbuf,"%c%c%s%c%s%c",0,1,“hello.txt”,0,"octet",0);//"octet"为二进制模式,“netascii”为文本模式
sendto(sockfd,sendbuf,len,0,(struct sockaddr*)&dest_addr,sizeof(dest_addr));
发送完了立刻进行接收
do{
//接收服务器发送的内容
len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);
cmd = recv_buf[1];
if( cmd == 3 )//是否为数据包
{
//包编号是否和上次相等
if( (unsigned short)(p_num+1) == ntohs(*(unsigned short*)(recv_buf+2) ))
{
write(fd, recv_buf+4, len-4);
p_num = ntohs(*(unsigned short*)(recv_buf+2));
printf("recv:%d\n", p_num);//十进制方式打印包编号
}
recv_buf[1] = 4;//操作码+1从数据包变为确认包
sendto(sockfd, recv_buf, 4, 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
}
else if( cmd == 5 ) //是否为错误应答
{
close(fd);
close(sockfd);
unlink(argv[2]);
printf("error:%s\n", recv_buf+4);
return 0;
}
}while(len == 516); //如果收到的数据小于516则认为出错
close(fd);
close(sockfd);
原文地址:http://blog.51cto.com/13603157/2095500