您的位置:首页 > 运维架构 > Linux

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

2017-01-20 04:16 429 查看

(一) 前情

在第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--
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux socket 网络