网络骇客入门之UDP编程


本文列出了在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);