Linux应用编程—14.UDP服务器、客户端编程
之前有介绍过UDP是一种无连接、尽最大努力交付、面向报文的协议。应用层交给UDP多长的报文,UDP就照样发送。Linux下UDP属于数据报socket。数据报socket流程图如图1所示:
新引入的函数recvfrom()作用是在一个数据报socket上接收数据。函数原型为ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);函数调用需要包含头文件sys/socket.h。参数1是之前的socket()返回的sockfd文件描述符;参数2buffer是接收数据缓存地址;参数3len是缓存地址大小;参数4flags是一个位掩码,决定了socket特定的I\O特性,可以写入默认值0;参数5src_addr和addrlen是用来获取或指定和它通讯的socket的地址。如果不关心发送者的地址,可以写入NULL。
函数sendto()作用是在一个数据报上发送数据。函数原型为ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen);该函数的dest_addr与addrlen参数指定了数据要发送到的目标socket。
14.1 UDP编程实战
基于UDP编写客户端与服务器的代码,服务器在while(1)中调用recvfrom()函数接收来自客户端的数据报并且打印。客户端在运行后,发送一条字符串“Hi, I’m client!”。
服务器代码如下所示:
include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define FAILE -1
#define SUCCESS 0
#define PORT 8800
#define SIZE 100
int main(void)
{
int ret = 0;
int sock_fd, client_sockfd = 0;
int addr_len = sizeof(struct sockaddr);
char str[SIZE];
struct sockaddr_in my_sockaddr, client_addr;
my_sockaddr.sin_family = AF_INET;
my_sockaddr.sin_port = htons(PORT);
my_sockaddr.sin_addr.s_addr = INADDR_ANY;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd == FAILE)
perror("socket.");
ret = bind(sock_fd, (struct sockaddr *)&my_sockaddr, addr_len);
if(ret == FAILE)
perror("bind.");
while(1)
{
printf("Server is waiting for client to connect:\n");
ret = recvfrom(sock_fd, str, SIZE, 0, (struct sockaddr *)&client_addr, &addr_len);
if(ret == FAILE)
perror("recvfrom.");
printf("Server receive from client: %s\n", str);
}
close(sock_fd);
return 0;
}
编译出可执行文件SERVER,如果没有收到客户端的数据报,则阻塞获取。
客户端代码如下所示:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define FAILE -1
#define SUCCESS 0
#define PORT 8800
#define SIZE 100
int main(int argc, char * argv[])
{
int ret = 0;
int sock_fd = 0;
struct sockaddr_in server_addr;
char buff[SIZE];
char *str = "Hi, I'm Client!";
if(argc < 2)
{
printf("Usage: ./client [server IP address.\n]");
}
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd == FAILE)
perror("socket.");
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
strcpy(buff, str);
printf("Client sends to server %s\n", buff);
ret = sendto(sock_fd, buff, SIZE, 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
close(sock_fd);
return 0;
}
编译出可执行文件 CLIENT。运行SERVER后,通过指令ifconfig查看虚拟机IP,然后运行./CLIENT 192.168.20.129。运行结果如下:
14.2 总结
UDP是无连接、尽最大努力交付、面向报文的通讯协议。UDP编程时,相对TCP简单,少了几个步骤。UDP编程使用socket 数据报,在建立socket时需要将流socket—SOCK_STREAM改为数据报socket—SOCK_DGRAM。