该函数提供的是一个迭代服务器,而不是像TCP服务器那样可以提供一个并发服务器。其中没有对fork的调用,因此单个服务器进程就得处理所有客户。一般来说,大多数TCP服务器是并发的,而大多数UDP服务器是迭代的。

对于本套接字,UDP层中隐含有排队发生。事实上每个UDP套接字都有一个接收缓冲区,到达该套接字的每个数据报都进入这个套接字接收缓冲区。当进程调用recvfrom时,缓冲区中的下一个数据报以FIFO(先入先出)顺序返回给进程。

服务器程序:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>#define SERV_PORT 3333
#define MAXLINE 1024#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \} while (0)typedef struct sockaddr SA;
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);}
}int
main(int argc, char **argv)
{int                    sockfd;struct sockaddr_in   servaddr, cliaddr;sockfd = socket(AF_INET, SOCK_DGRAM, 0);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));
}

客户端程序:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>#define SERV_PORT 3333
#define MAXLINE 1024
#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)typedef struct sockaddr SA;
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);}
}int main(int argc, char **argv)
{int                    sockfd;struct sockaddr_in   servaddr;if (argc != 2)ERR_EXIT("usage: udpcli <IPaddress>");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);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);
}

1.数据报的丢失

我们的UDP客户/服务器例子是不可靠的。如果一个客户数据报丢失(譬如说,被客户主机与服务器主机之间的某个路由器丢失),客户将永远阻塞于dg_cli函数中的recvfrom调用,等待一个永远不会达到的服务器应答。类似的,如果客户数据报到达服务器,但是服务器的应答丢失了,客户也将永远阻塞于recvfrom调用。防止这样永远阻塞的一般方法是给客户的recvfrom调用设置一个超时。

仅仅给recvfrom调用设置超时并不是完整的解决办法。举例来说,如果确实超时了,我们将无从判定超时原因是我们的数据报没有到达服务器,还是服务器的应答没有回到客户。所以我们可以增加UDP客户/服务器程序的可靠性。(后面会有讲解)

2.服务器进程未运行

我们下一个要检查的情形是在不启动服务器的前提下启动客户。如果我们这么做后在客户上键入一行文本,那么什么也不发生。客户永远阻塞于它的recvfrom调用,等待一个永远不出现的服务器应答。

经过抓包分析,服务器主机响应的是一个“port unreachable”(端口不可达)ICMP消息。不过这个ICMP错误不返回给客户进程。我们称这个ICMP错误为异步错误。该错误由sendto引起,但是sendto本身却成功返回。我们知道从UDP输出操作成功返回仅仅表示在输出队列中具有存放所形成IP数据报的空间。该ICMP错误直到后来才返回,这就是称其为异步的原因。

一个基本规则是:对于一个UDP套接字,由它引发的异步错误却并不返回给它,除非它已连接。仅在进程已将其UDP套接字连接到恰恰一个对端后,这些异步错误才返回给进程。

注:只要SO_BSDCOMPAT 套接字选项没有开启,linux甚至对未连接的套接字也返回大多数ICMP(目的地不可达)错误。

3.验证接收到的响应

知道客户临时端口号的任何进程都可往客户发送数据报,而且这些数据报会与正常的服务器应答混杂。

我们的解决办法是修改recvfrom调用以返回数据报发送者的IP地址和端口号,保留来自数据报所发往服务器的应答,而忽略任何其他数据报。

void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{int                n;char          sendline[MAXLINE], recvline[MAXLINE + 1];socklen_t     len;struct sockaddr_in  *preply_addr;preply_addr = malloc(servlen);while (fgets(sendline, MAXLINE, fp) != NULL) {sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);len = servlen;n = recvfrom(sockfd, recvline, MAXLINE, 0, (SA*)preply_addr, &len);if (len != servlen || memcmp(pservaddr, (SA*)preply_addr, len) != 0) {printf("reply from %s (ignored)\n",inet_ntoa(preply_addr->sin_addr));continue;}recvline[n] = 0;   /* null terminate */fputs(recvline, stdout);}
}

然而这样做照样存在一些缺陷,如果服务器运行在一个只有单个IP地址的主机上,那么这个版本的客户工作正常。然而如果服务器主机是多宿的(多个IP地址),该客户就有可能失败。例如服务器有2个IP地址(172.24.37.94和135.197.17.100),客户连接服务器(135.197.17.100),但是服务器响应的IP地址是(172.24.37.94),这样我们的程序就出问题。

一个解决办法是:得到由recvfrom返回的IP地址后,客户通过在DNS中查找服务器主机的名字来验证该主机的域名(而不是它的IP地址)。

另一个解决办法是:UDP服务器给服务器主机上配置的每个IP地址创建一个套接字,用bind捆绑每个IP地址到各自的套接字,然后再所有这些套接字上使用select(等待其中任何一个变得可读),再从可读的套接字给出应答。既然用于给出应答的套接字上绑定的IP地址就是客户请求的目的IP地址(否则该数据报不会被投递到达该套接字),这就保证应答的源地址与请求的目的地址相同。

转载于:https://www.cnblogs.com/wangfengju/p/6172564.html

UNIX网络编程——UDP回射服务器程序(初级版本)以及漏洞分析相关推荐

  1. 网络编程学习笔记(使用select函数的TCP和UDP回射服务器程序)

    一开始,创建完TCP后,没有用listen监听.启动服务器后,再启动TCP客户端,前几次总是打印readline error:success.后面就恢复正常了 服务器端: #include <s ...

  2. UNIX网络编程——UDP缺乏流量控制(改进版)

    现在我们查看无任何流量控制的UDP对数据报传输的影响.首先我们把dg_cli函数修改为发送固定数目的数据报,并不再从标准输入读.如下,它写2000个1400字节大小的UDP数据报给服务器. 客户端程序 ...

  3. unix网络编程各种TCP客户-服务器程序设计实例(三)

    第五种  TCP预先派生子进程服务器程序: 对预先派生子进程服务器的最后一种改动就是由父进程调用accept,然后再将所接受的已连接描述字传递给子进程.父进程必须跟踪子进程的忙闲状态,以便给空闲子进程 ...

  4. UNIX网络编程——使用线程的TCP回射服务器程序

    同一进程内的所有线程除了共享全局变量外还共享: (1)进程指令: (2)大多数数据: (3)  打开的文件(即描述符): (4)信号处理函数和信号处置: (5)当前工作目录: (6)用户ID和组ID. ...

  5. 网络编程学习笔记(TCP回射服务器程序修订版)

    服务器端: #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include ...

  6. 《UNIX网络编程:套接字联网API》啃书笔记(第8UDP套接字编程、11章地址转换)

    基本UDP套接字编程 下图为UDP客户/服务器程序的函数调用: 注意客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数.类似的,服务器不接受来自客 ...

  7. 【UNIX网络编程】| 【03】TCP客户/服务器程序示例

    文章目录 1.概述 2.TCP回射服务器程序 3.TCP回射客户程序 3.正常启动 4.正常终止 5.POSIX信号处理 5.1 signal函数 5.2 POSIX信号语义 6.处理SIGCHID信 ...

  8. C++教程网之Linux网络编程视频 Unix网络编程视频

    教程非常不错,价值280元,绝对是干货 Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章. Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 ...

  9. UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订)...

    UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订) 基本信息 原书名: Unix Network Programming, Vol ...

最新文章

  1. php和 sqlserver效率,SQLServer语句执行效率及性能测试
  2. Ubuntu 17.04 Chrome 安装
  3. 单调队列 Monotonic Queue / 单调栈 Monotonic Stack
  4. eclipse插件开发_开发Eclipse插件
  5. 个人简介页面如何设计?集设网优秀案例给你灵感
  6. C++复数的运算、运算符重载
  7. MVC模式中编写一个登录的Servlet
  8. 搜狗手机输入法java_手机搜狗输入法制作个性皮肤的方法
  9. 三星固态sm863,pm863,sm865,sm865a颗粒
  10. 苹果x和xs买哪个好_iPhone XS和iPhone XR买哪个好?苹果XR和XS区别对比测评
  11. TDSQL将发布免费版本,助力国产数据库生态完善
  12. David Lowe 的sift代码
  13. uview 瀑布流_微信小程序 瀑布流布局
  14. 黑莓android系统,黑莓Passport的手机系统是什么?能升级安卓5.0吗?
  15. 网络附加存储(NAS)
  16. sh股票是什么意思?
  17. 笔记本电脑连接无线网卡如何开热点
  18. 保险业的5项CX预测
  19. ps打不开图片显示计算机丢失,电脑的ps软件打不开图片怎么解决
  20. 网络——tcp/ip

热门文章

  1. Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用...
  2. PHP ‘ext/soap/soap.c’权限许可和访问控制
  3. 28、FileThumbnails
  4. 内存溢出原因及解决方案
  5. 浅谈创业性公司的发展
  6. Python中print()使用格式示例收集
  7. leetcode算法题--Perfect Squares
  8. leetcode算法题--字符串相乘
  9. MYSQL数据库备份还原,并还原到最新状态(mysqldump,xtrabackup)
  10. Exp2 后门原理与实践 20164323段钊阳