UNP学习笔记3——基本UDP套接字编程

1 概述

TCP和UDP网络编程存在一些本质的差异,主要是由于传输层的差别:UDP是无连接的不可靠的数据报协议,而TCP是面向连接的字节流协议。

下图是典型的UDP客户端和服务器之间的通信流程。客户不与服务器建立连接,而是只管使用sendto函数。服务器不接受来自客户的连接,而是只管调用recvfrom函数,等待某个客户的数据到达。

本章学习用于UDP套接字的两个新函数recvfrom和sendto,并使用UDP重写ECHO程序。还将学习connect函数在UDP套接字中的用法,同时理解异步错误的概念

2 recvfrom和sendto函数

这两个函数类似标准的read、write函数,不过需要额外的三个参数。

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen);
/*以上函数返回:成功返回读或写到的字节数,出错返回-1*/

 

前三个参数sockfd、buff和nbytes分别代表:描述符、指向输入或写出缓冲区的指针和读写的字节数。

flags参数在讨论recv、send、recvmsg和sendmsg函数时再介绍,这里默认设置为0。

sendto的to参数指向一个含有数据报接受者的协议地址(IP地址及端口号)的套接字地址结构,其大小由addrlen指定。recvfrom的from参数指向一个含有数据报发送者的协议地址(IP地址及端口号)的套接字地址结构,其大小由addrlen指定。注意sendto的最后一个参数是一个整数值,而recvfrom的最后一个参数是一个指向整形的指针(即值-结果函数)。

写一个长度位(0)的数据报是可行的(TCP不允许),这会发送一个只有IP首部和UDP首部,没有数据的数据报。类似的,recvfrom返回0值也是可以的(TCP则代表对端已关闭连接)。如果recvfrom的from参数是一个空指针,相应的addrlen为0,说明我们不关心发送者的协议地址。recvfrom和sendto函数都可以用于TCP,尽管通常不这么做。

3 最原始的UDP Echo程序

 udp_echo_server.c

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

void echo_server(int sockfd, struct sockaddr *p_client_addr, socklen_t cli_len) {
    
    int n;
    socklen_t len;
    char buf[1024];
    
    while(1) {
        len = cli_len;
        n = recvfrom(sockfd, buf, 1024, 0, p_client_addr, &len);
        sendto(sockfd, buf, n, 0, p_client_addr, len);
    }
}


int main(int argc, char **argv) {
    
    int sockfd;
    struct sockaddr_in server_addr, client_addr;
    
    sockfd=socket(AF_INET, SOCK_DGRAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    server_addr.sin_port=htons(12345);
    
    if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))<0)
        perror("bind error!");
    
    echo_server(sockfd, (struct sockaddr *)&client_addr, sizeof(client_addr));
        
}

 

该程序存在一些问题:

  1. 函数永远不会终止,因为UDP是一个无连接的协议,没有像TCP中EOF之类的东西
  2. 该函数提供的是一个迭代服务器,而不是并发服务。因此单个服务器进程就得处理所有客户
  3. 对于本套接字,UDP层隐含有排队的发生

udp_echo_client.c

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

void echo_client(FILE *fp, int sockfd, const struct sockaddr *p_server_addr, socklen_t serv_len) {
    
    int n;
    char send[1024], recv[1024];
    while(fgets(send, 1024, fp) !=NULL) {
        sendto(sockfd, send, strlen(send), 0, p_server_addr, serv_len);
        n=recvfrom(sockfd, recv, 1024, 0, NULL, NULL);
        recv[n]='\0';
        fputs(recv, stdout);
    }
}

int main(int argc, char **argv) {
    int sockfd;
    struct sockaddr_in server_addr;
    
    if(argc!=2) {
        printf("usage: udp_client <IP Address>\n");
        exit(0);
    }
    
    sockfd=socket(AF_INET, SOCK_DGRAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(12345);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);    
    
    echo_client(stdin, sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
}

 

该程序也隐含一个问题:

  1. 客户端尚未请求内核给它的套接字指派一个临时端口
  2. recvfrom第5个和第6个参数是空指针,表示不关心数据是由谁发送。因此任何进程的数据包都会被当成是服务器的应答。
  3. UDP是不可靠的。如果一个数据报丢失,客户将永远阻塞在recvfrom函数上。

假设在服务器不启动的情况下启动客户端,数据报由客户发出,服务器主机响应一个端口不可达的ICMP消息,而这个ICMP消息不返回给客户进程,客户将永远阻塞于recvfrom调用,等待服务器的应答。我们称这个错误为异步错误(asynchronous error)。该错误由sendto引起,但sendto本身却成功返回。UDP输出成功仅仅代表在接口队列中有存放IP数据报的空间。改ICMP直到后来才返回,这就是称其为异步的原因。

4 UDP程序例子小结

 

转载于:https://www.cnblogs.com/alwayswangzi/p/6595310.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值