基本UDP套接字编程
概述
UDP是无连接不可靠的数据报协议,不同于TCP提供的面向连接的可靠字节流。常用的应用包括:DNS(域名系统),NFS(网络文件系统),SNMP(简单网络管理协议)。
图中给出了典型的UDP客户/服务器的函数调用。客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地,(即服务器)的地址作为参数。类似的,服务器不接受来自客户端的连接,而是只管调用recvfrom函数,等待来自某个客户的数据到达。recvform将所接收的数据报一道返回客户的协议地址,因此服务器可以把响应发送给正确的客户。
recvfrom 和sendto函数
这两个函数类似于标准的read 和write函数,不过需要额外的三个参数
#include <sys/socket.h>
ssize_t recvform(int sockfd, void *buff , size_t nbytes, int flags ,
struct sockaddr *from , socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff , size_t nbytes, int flags ,
struct sockaddr *to , socklen_t addrlen);//若成功则返回读或写的字节大小,若出错返回-1
前三个参数等同于read和write的三个参数,分别为:描述符、指向读入或写出缓冲区的指针和读写字节数。
flags会在后续讨论recv,send等函数时说明,目前暂时都设为0。
recvfrom的from参数指向一个将由该函数在返回时填写数据报发送者的协议地址的套接字地址结构(从哪里接收),其大小由addrlen指定;
sendto的to参数指向一个含有数据报接收者的协议地址的套接字地址结构(发到哪里去),其大小由addrlen指定。
注意:sendto的最后一个参数是一个整型值,而recvfrom则是一个指向整数值的指针。
不同于TCP在read返回0时表示对端关闭连接,UDP可以写一个长度为0的数据报,接受的数据报也可以长度为0,UDP没有关闭一个连接的概念。
如果recvfrom的from参数是一个空指针,那么相应的长度参数也必须是一个空指针,表示我们并不关心数据发送者的协议地址。
UDP回射程序
使用UDP来重写之前TCP的回射程序,函数流程图如下图所示。
UDP回射服务器程序如下所示:
#include "unp.h"
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);//UDP面向数据报,SOCK_DGRAM为数据报套接字
bzero(&servaddr, sizeof(servaddr));//初始化套接字
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));//将本地协议地址赋值给套接字
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));//调用回射程序
}
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
for ( ; ; ) {
len = clilen;
n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);//接受客户的消息
Sendto(sockfd, mesg, n, 0, pcliaddr, len);//回射给客户端
}
}
UDP回射客户程序如下所示:
#include "unp.h"
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: udpcli <IPaddress>");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(7);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);//将点分十进制地址转换成二进制地址
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);//建立套接字
dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));//调用回射程序
exit(0);
}
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];
while (Fgets(sendline, MAXLINE, fp) != NULL) {//从标准输入读入数据
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);//将文本发送给服务器
n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);//读取回射后的文本
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);//标准输出
}
}
测试结果
ubuntu15.10环境下,测试上述代码:
@Inspiron-N4010:~/unp$ ./udpcli 127.0.0.1
hello
hello
回射服务器正确返回客户端发送的数据
转载自:https://zcheng.ren/posts/UnixNetworkProgrammingPart6/#disqus_thread