例3-6说明了怎样通过套接字标识符在发送数据时指定目标地址及端口并发送UDP数据报。
例3-6 用sendto( )函数发送UDP数据报(udp3.c)
1 /*
2 * udp3.c
3 *
4 * send UDP datagram using socket
5 * descriptor and sendto(). example
6 * program #3.
7 *
8 * foster <jamescfoster@gmail.com>
9 */
10
11 #include <stdio.h>
12
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16
17 #define UDP3_DST_ADDR "127.0.0.1"
18 #define UDP3_DST_PORT 1234
19
20 int
21 main(void)
22 {
23 struct sockaddr_in sin;
24 char buf[100];
25 int sock = 0;
26 int ret = 0;
27
28 sock = socket(AF_INET, SOCK_DGRAM, 0);
29 if(sock < 0)
30 {
31 printf("socket() failed.\n");
32 return(1);
33 }
34
35 memset(&sin, 0x0, sizeof(sin));
36
37 sin.sin_family = AF_INET;
38 sin.sin_port = htons(UDP3_DST_PORT);
39 sin.sin_addr.s_addr = inet_addr(UDP3_DST_ADDR);
40
41 memset(buf, 'A', 100);
42
43 ret = sendto(sock, buf, 100, 0,
44 (struct sockaddr *) &sin, sizeof(sin));
45 if(ret != 100)
46 {
47 printf("sendto() failed.\n");
48 return(1);
49 }
50
51 close(sock);
52 printf("sendto() success.\n");
53
54 return(0);
55 }
编译
obsd32# gcc -o udp3 udp3.c
执行
obsd32# ./udp3
sendto() success.
分析
udp3.c示例了使用sendto( )函数发送数据的另一种可选方法。使用sendto( )函数发送数据时,数据的目标地址是由每次调用时的第5个参数(sockaddr_in型的结构)指定的,而不是事先调用connect( )函数,这样允许使用一个套接字描述符发送数据到不同的目标地址。当数据必须发送到不同的目标时,如实现基于UDP的扫描器,sendto( )函数将十分有用。
udp3.c与udp2.c之间的区别在于没有调用connect( )函数,并且用sendto( )函数代替了send( )函数。例3-7说明了怎样使用recvfrm( )函数接收UDP数据报。
例3-7 接收UDP数据报(udp4.c)
1 /*
2 * udp4.c
3 *
4 * receive UDP datagram using
5 * recvfrom() function. example
6 * program #4.
7 *
8 * foster <jamescfoster@gmail.com>
9 */
10
11 #include <stdio.h>
12
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15
16 #define UDP4_PORT 1234
17
18 int
19 main(void)
20 {
21 struct sockaddr_in sin;
22 char buf[100];
23 int sock = 0;
24 int ret = 0;
25
26 sock = socket(AF_INET, SOCK_DGRAM, 0);
27 if(sock < 0)
28 {
29 printf("socket() failed.\n");
30 return(1);
31 }
32
33 memset(&sin, 0x0, sizeof(sin));
34
35 sin.sin_family = AF_INET;
36 sin.sin_port = htons(UDP4_PORT);
37 sin.sin_addr.s_addr = INADDR_ANY;
38
39 ret = bind(sock, (struct sockaddr *) &sin, sizeof(sin));
40 if(ret < 0)
41 {
42 printf("bind() failed.\n");
43 return(1);
44 }
45
46 ret = recvfrom(sock, buf, 100, 0, NULL, NULL);
47 if(ret < 0)
48 {
49 printf("recvfrom() failed.\n");
50 return(1);
51 }
52
53 close (sock);
54 printf("recvfrom() success.\n");
55
56 return(0);
57 }
编译
obsd32# gcc -o udp4 udp4.c
执行
obsd32# ./udp4 &
[1] 18864
obsd32# ./udp3
recvfrom() success.
sendto() success.
[1] + Done ./udp4
程序udp4.c创建了一个UDP套接字,将套接字绑定到1234号端口,并等待接收UDP数据报。在执行过程中,首先执行程序udp4,之后再执行程序udp3。程序udp3向程序udp4发送了一个UDP数据报。
分析
● 第13行和第14行,载入sys/socket.h和netinet/in.h两个头文件。
● 第16行,声明UDP套接字将要绑定的端口,在本例中是1234。
● 第26行,使用socket()函数创建一个套接字标识符,与前面的示例相似。
● 第32行到第36行,初始化sockaddr_in结构,用来指定将要绑定该套接字的本地端点的IP地址和端口,其中sin_family和sin_port两个成员的处理方法与前面的示例相同,sin_addr.s_addr成员赋值为整型常量INADDR_ANY,表示将套接字绑定到系统中任意可用的IP地址上。例如,如果程序运行在一台有两个网络接口的主机上,每个接口都有不同的IP地址,则套接字将绑定到这两个接口上,并且在两个接口上都是可用的。将指定的网络接口的IP地址赋值给sin_addr.s_ addr成员,也可以将套接字绑定到单个的网络接口上。
● 第39行,调用bind()函数,将套接字绑定到sockaddr_in结构中所定义的本地端点。第1个参数为要绑定的套接字标识符;第2个参数为sockaddr_in结构的地址,必须转换为sockaddr结构指针类型;第3个参数为sockaddr_in结构的字节数。如果执行成功,bind()函数将返回一个非负整型值;如果发生错误,则返回一个负的整型值。
● 第46行,使用recvfrom()函数接收单个UDP数据报。其中,传给函数的第1个参数为绑定的套接字标识符;第2个参数为缓冲区地址,用来存储接收到的数据;第3个参数为缓冲区的长度;第4个参数可以是一个指向sockaddr_in结构并转换为sockaddr*类型的指针;第5个参数可以是一个整型指针,指向一个表示sockaddr_in结构长度的整数(以字节为单位)。
● 第53行,套接字被close()函数关闭,不可再用来接收数据。






