【Linux Socket 编程入门】06 - 拉个骡子溜溜:UDP编程模型代码分析

(一) 前情

在第4篇里面,介绍了TCP编程实例,现在,我们再看看UDP编程实例。才完美嘛。


(二)上个菜:一个UDP程序分析

开胃:UDP客户服务器编程模型

与TCP面向连接,可靠的编程模型不同,UDP面向数据报,是不可靠的编程模型。因此它的编程模型相较于TCP,会简单一些。


上菜:UDP编程实例分析

服务器端程序:(可以参照上面的编程模型,理解下面这段代码)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define MYPORT "4950" //端口号
#define MAXBUFLEN 100 //buffer的最大长度

//get sockaddr, IPv4 or IPv6
void *get_in_addr(struct sockaddr *sa)
{
	if(sa->sa_family == AF_INET) {
		return &(((struct sockaddr_in *)sa)->sin_addr);
	}
	return &(((struct sockaddr_in6 *)sa)->sin6_addr);
}

int main(void)
{
	int sockfd;
	struct addrinfo hints, *servinfo, *p;
	struct sockaddr_storage their_addr;
	socklen_t addr_len;
	char s[INET6_ADDRSTRLEN];
	char buf[MAXBUFLEN];
	int rv;
	int numbytes;

	memset(&hints, 0, sizeof(hints)); //一定先清0整个结构体。
	hints.ai_family = AF_UNSPEC; //自动选择IPv4或者IPv6
	hints.ai_socktype = SOCK_DGRAM;  //采用UDP编程模型
	hints.ai_flags = AI_PASSIVE; // 填充本机IP

	if(0 != (rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo))) { //填充addrinfo结构体。
		fprintf(stderr, "getaddrinfo:%s\n", gai_strerror(rv));
		return 1;
	}

	//for循环,选择第一个可用的IP地址。
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if(-1 == (sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))) {
			perror("listener: socket");
			continue;
		}

		if(-1 == (bind(sockfd, p->ai_addr, p->ai_addrlen))) {
			close(sockfd); //绑定失败,一定要close(),释放已经分配的网络描述符。
			perror("listener: bind");
			continue;

		}

		break;
	}	

	if(NULL == p) {
		fprintf(stderr, "listener: failed to bind\n");
		return 2;
	}
	
	freeaddrinfo(servinfo); // 后面不会再使用servinfo,可以释放掉。

	printf("listener: waiting to recvfrom...\n");

	addr_len = sizeof(their_addr);
	if(-1 == (numbytes = recvfrom(sockfd, buf, MAXBUFLEN - 1, 0, (struct sockaddr *)&their_addr, &addr_len))) {//绑定成功后,直接recvfrom(),等待数据。
		perror("recvfrom");
		exit(1);
	}
	
	printf("listener: got packet from %s\n", inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof(s)));
	printf("listener: packet is %d bytes long\n", numbytes);
	buf[numbytes] = '\0';
	printf("listener: packet contains \"%s\"\n", buf);//打印接收到的数据。

	close(sockfd);
	return 0;
}


客户端程序:(可以参照上面的编程模型,理解下面这段代码)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define SERVERPORT "4950" //定义端口号,必须与服务器端相等。

int main(int argc, char *argv[])
{
	int sockfd;
	struct addrinfo hints, *servinfo, *p;
	int rv;
	int numbytes;
	
	if(argc != 3) {
		fprintf(stderr, "usage: talker hostname message\n");
		exit(1);
	}	

	memset(&hints, 0, sizeof(hints)); //首先,清0结构体。
	hints.ai_family = AF_UNSPEC; //自动选择IPv4,IPv6.
	hints.ai_socktype = SOCK_DGRAM; //采用UDP protocol.

	if(0 != (rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo))) { //填充addrinfo结构体
		fprintf(stderr, "getaddrinfo:%s\n", gai_strerror(rv));
		return 1;
	}

	for循环,选择第一个可用的IP地址。
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if(-1 == (sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))) {
			perror("talker: socket");
			continue;
		}

		break;
	}	

	if(NULL == p) {
		fprintf(stderr, "talker: failed to create socket\n");
		return 2;
	}

	if(-1 == (numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0, p->ai_addr, p->ai_addrlen))) { // 啥也不用做,直接sendto()发送数据
		perror("talker: sendto");
		exit(1);
	}

	freeaddrinfo(servinfo);
	printf("talker: send %d bytes to %s\n", numbytes, argv[1]);
	close(sockfd);

	return 0;
}



测试步骤:
测试的环境为:ubuntu 16.04

利用gcc分别编译客户端程序和服务器端程序。编译完成后,先启动服务器端程序(假设在当前目录下),在shell下执行:
./server
然后再启动客户端程序:打开另一个终端,在shell中执行(利用loop back IP address进行测试):
./client 127.0.0.1 "Hello, World!"

测试结果:
在服务器端会看到如下信息:
listener: waiting to recvfrom...
listener: got packet from 127.0.0.1
listener: packet is 12 bytes long
listener: packet contains "Hello World!"


在客户端会看到如下信息:
talker: send 12 bytes to 127.0.0.1

--THE END--

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值