在网络编程中,UDP运用非常广泛。很多网络协议是基于UDP来实现的,如SNMP等。大家常常用到的局域网文件传输软件飞鸽传书也是基于UDP实现的。

本篇文章跟大家分享linux下UDP的使用和实现,主要介绍下sendto()和recvfrom()两个函数的使用,以及INADDR_ANY的说明,并在最后展示了一个经过自己测试可用的UDP Server和UDP Client的代码示例。

头文件

#include <sys/types.h>
#include <sys/socket.h>

函数原型

int sendto (int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);

int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);

 

函数说明

sendto(),是把UDP数据报发给指定地址;recvfrom()是从指定地址接收UDP数据报。

参数说明

  • \s:              socket描述符。
  • \buf:         UDP数据报缓存地址。
  • \len:          UDP数据报长度。
  • \flags:       该参数一般为0。
  • \to:            sendto()函数参数,struct sockaddr_in类型,指明UDP数据发往哪里报。
  • \tolen:      对方地址长度,一般为:sizeof(struct sockaddr_in)。
  • \fromlen:recvfrom()函数参数,struct sockaddr_in类型,指明从哪里接收UDP数据报。

函数返回值

对于sendto()函数,成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中。

对于recvfrom()函数,成功则返回接收到的字符数,失败则返回-1,错误原因存于errno中。

struct sockaddr_in结构体

该结构体的定义如下:

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
struct sockaddr_in {
  sa_family_t		sin_family;	/* Address family		*/
  __be16		sin_port;	/* Port number			*/
  struct in_addr	sin_addr;	/* Internet address		*/

  /* Pad to size of `struct sockaddr'. */
  unsigned char		__pad[__SOCK_SIZE__ - sizeof(short int) -
			sizeof(unsigned short int) - sizeof(struct in_addr)];
};

其中,sin_family指明地址族,一般使用AF_INET:

#define AF_INET		2	/* Internet IP Protocol 	*/

sin_port:指明UDP端口;sin_addr指明IP地址:

/* Internet address. */
struct in_addr {
	__be32	s_addr;
};

INADDR_ANY说明

INADDR_ANY,是个特殊IP地址 ,表示任务的IP地址,作为服务器端的时候经常要用到。对于它的解释,摘用下面一段英文(来自于:http://www.cs.cmu.edu/~srini/15-441/F01.full/www/assignments/P2/htmlsim_split/node18.html):

When you wrote your simple FTP server in project 1, you probably bound your listening socket to the special IP address INADDR_ANY. This allowed your program to work without knowing the IP address of the machine it was running on, or, in the case of a machine with multiple network interfaces, it allowed your server to receive packets destined to any of the interfaces. In reality, the semantics of INADDR_ANY are more complex and involved.

In the simulator, INADDR_ANY has the following semantics: When receiving, a socket bound to this address receives packets from all interfaces. For example, suppose that a host has interfaces 0, 1 and 2. If a UDP socket on this host is bound using INADDR_ANY and udp port 8000, then the socket will receive all packets for port 8000 that arrive on interfaces 0, 1, or 2. If a second socket attempts to Bind to port 8000 on interface 1, the Bind will fail since the first socket already “owns” that port/interface.

When sending, a socket bound with INADDR_ANY binds to the default IP address, which is that of the lowest-numbered interface.

大概的意思就是,作为接收端,当你调用bind()函数绑定IP时使用INADDR_ANY,表明接收来自任意IP、任意网卡的发给指定端口的数据。作为发送端,当用调用bind()函数绑定IP时使用INADDR_ANY,表明使用网卡号最低的网卡进行发送数据,也就是UDP数据广播。

关于UDP数据报

UDP都是以数据报的形式进行发送和接收的,而TCP是以数据流的形式进行发送和接收的。数据报和数据流,这两者要区分开来。

UDP Server和Client源码实例

UDP Server:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define	UDP_TEST_PORT		50001

int main(int argC, char* arg[])
{
	struct sockaddr_in addr;
	int sockfd, len = 0;	
	int addr_len = sizeof(struct sockaddr_in);
	char buffer[256];	

	/* 建立socket,注意必须是SOCK_DGRAM */
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror ("socket");
		exit(1);
	}

	/* 填写sockaddr_in 结构 */
	bzero(&addr, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(UDP_TEST_PORT);
	addr.sin_addr.s_addr = htonl(INADDR_ANY) ;// 接收任意IP发来的数据

	/* 绑定socket */
	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr))<0) {
		perror("connect");
		exit(1);
	}

	while(1) {
		bzero(buffer, sizeof(buffer));
		len = recvfrom(sockfd, buffer, sizeof(buffer), 0, 
					   (struct sockaddr *)&addr ,&addr_len);
		/* 显示client端的网络地址和收到的字符串消息 */
		printf("Received a string from client %s, string is: %s\n", 
				inet_ntoa(addr.sin_addr), buffer);
		/* 将收到的字符串消息返回给client端 */
		sendto(sockfd,buffer, len, 0, (struct sockaddr *)&addr, addr_len);
	}

	return 0;
}

// ----------------------------------------------------------------------------
// End of udp_server.c

UDP Client:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define	UDP_TEST_PORT		50001
#define UDP_SERVER_IP 		"127.0.0.1"

int main(int argC, char* arg[])
{
	struct sockaddr_in addr;
	int sockfd, len = 0;	
	int addr_len = sizeof(struct sockaddr_in);		
	char buffer[256];

	/* 建立socket,注意必须是SOCK_DGRAM */
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		exit(1);
	}

	/* 填写sockaddr_in*/
	bzero(&addr, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(UDP_TEST_PORT);
	addr.sin_addr.s_addr = inet_addr(UDP_SERVER_IP);

	while(1) {
		bzero(buffer, sizeof(buffer));

		printf("Please enter a string to send to server: \n");

		/* 从标准输入设备取得字符串*/
		len = read(STDIN_FILENO, buffer, sizeof(buffer));

		/* 将字符串传送给server端*/
		sendto(sockfd, buffer, len, 0, (struct sockaddr *)&addr, addr_len);

		/* 接收server端返回的字符串*/
		len = recvfrom(sockfd, buffer, sizeof(buffer), 0, 
				       (struct sockaddr *)&addr, &addr_len);
		printf("Receive from server: %s\n", buffer);
	}

	return 0;
}

// ----------------------------------------------------------------------------
// End of udp_client.c

上述代码是经过验证可用的。

» 文章出处: reille博客—http://velep.com , 如果没有特别声明,文章均为reille博客原创作品
» 郑重声明: 原创作品未经允许不得转载,如需转载请联系reille#qq.com(#换成@)
分享到:

  One Response to “UDP sendto和recvfrom使用详解”

  1. int addr_len = sizeof(struct sockaddr_in);

    linux 上要求变量的类型是unsigned的

 Leave a Reply

(必须)

(我会替您保密的)(必须)

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

   
© 2012 velep.com | reille blog | 管理| 粤ICP备15065318号-2| 谷歌地图| 百度地图| Suffusion theme|Sayontan Sinha

无觅相关文章插件,快速提升流量