UNP学习笔记(第八章 基本UDP套接字编程)

UDP应用程序客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数。

下图给出典型的UDP客户/服务器程序的函数调用。

recvfrom和sendto函数

这两个函数类似于标准的read和write函数,不过需要3个额外的参数

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,const struct sockaddr *to, socklen_t addrlen);
                                                                         //均返回:若成功则为读或写的字节数,若出错则为-1

UDP回射服务器程序

main函数

 1 #include    "unp.h"
 2
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     struct sockaddr_in    servaddr, cliaddr;
 8
 9     sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
10
11     bzero(&servaddr, sizeof(servaddr));
12     servaddr.sin_family      = AF_INET;
13     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
14     servaddr.sin_port        = htons(SERV_PORT);
15
16     Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
17
18     dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
19 }

dg_echo函数

 1 #include    "unp.h"
 2
 3 void
 4 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
 5 {
 6     int            n;
 7     socklen_t    len;
 8     char        mesg[MAXLINE];
 9
10     for ( ; ; ) {
11         len = clilen;
12         n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
13
14         Sendto(sockfd, mesg, n, 0, pcliaddr, len);
15     }
16 }

UDP回射客户程序

main函数

 1 #include    "unp.h"
 2
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     struct sockaddr_in    servaddr;
 8
 9     if (argc != 2)
10         err_quit("usage: udpcli <IPaddress>");
11
12     bzero(&servaddr, sizeof(servaddr));
13     servaddr.sin_family = AF_INET;
14     servaddr.sin_port = htons(SERV_PORT);
15     Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
16
17     sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
18
19     dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
20
21     exit(0);
22 }

dg_cli函数

 1 #include    "unp.h"
 2
 3 void
 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
 5 {
 6     int    n;
 7     char    sendline[MAXLINE], recvline[MAXLINE + 1];
 8
 9     while (Fgets(sendline, MAXLINE, fp) != NULL) {
10
11         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
12
13         n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
14
15         recvline[n] = 0;    /* null terminate */
16         Fputs(recvline, stdout);
17     }
18 }

验证接收到的相应

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

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

下面是验证返回套接字地址的dg_cli函数版本

 1 #include    "unp.h"
 2
 3 void
 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
 5 {
 6     int                n;
 7     char            sendline[MAXLINE], recvline[MAXLINE + 1];
 8     socklen_t        len;
 9     struct sockaddr    *preply_addr;
10
11     preply_addr = Malloc(servlen);
12
13     while (Fgets(sendline, MAXLINE, fp) != NULL) {
14
15         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
16
17         len = servlen;
18         n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
19         if (len != servlen || memcmp(pservaddr, preply_addr, len) != 0) {
20             printf("reply from %s (ignored)\n",
21                     Sock_ntop(preply_addr, len));
22             continue;
23         }
24
25         recvline[n] = 0;    /* null terminate */
26         Fputs(recvline, stdout);
27     }
28 }

UDP的connect函数

UDP套接字可以调用connect,但是跟TCP套接字不一样,它不会有三次握手过程。

对于已连接的套接字,与默认的未连接套接字相比,发生了三个变化:

1.我们再也不能给输出操作指定目的IP地址和端口号。也就是说,我们不使用sendto而改用write或send。

2.我们并不比使用recvfrom以获悉数据报的发送者,而改用read、recv或recvmsg。

3.由已连接UDP套接字引发的异步错误会返回给它们所在的进程,而未连接UDP套接字不接受任何异步错误。

一个已连接的UDP套接字可以再次调用connect以用于:

1.指定新的IP地址和端口号

2.断开套接字

dg_cli函数(修订版)

把上面dg_cli函数重写成调用connect的新函数

 1 #include    "unp.h"
 2
 3 void
 4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
 5 {
 6     int        n;
 7     char    sendline[MAXLINE], recvline[MAXLINE + 1];
 8
 9     Connect(sockfd, (SA *) pservaddr, servlen);
10
11     while (Fgets(sendline, MAXLINE, fp) != NULL) {
12
13         Write(sockfd, sendline, strlen(sendline));
14
15         n = Read(sockfd, recvline, MAXLINE);
16
17         recvline[n] = 0;    /* null terminate */
18         Fputs(recvline, stdout);
19     }
20 }

使用select的TCP和UDP回射服务器程序

 1 /* include udpservselect01 */
 2 #include    "unp.h"
 3
 4 int
 5 main(int argc, char **argv)
 6 {
 7     int                    listenfd, connfd, udpfd, nready, maxfdp1;
 8     char                mesg[MAXLINE];
 9     pid_t                childpid;
10     fd_set                rset;
11     ssize_t                n;
12     socklen_t            len;
13     const int            on = 1;
14     struct sockaddr_in    cliaddr, servaddr;
15     void                sig_chld(int);
16
17         /* 4create listening TCP socket */
18     listenfd = Socket(AF_INET, SOCK_STREAM, 0);
19
20     bzero(&servaddr, sizeof(servaddr));
21     servaddr.sin_family      = AF_INET;
22     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
23     servaddr.sin_port        = htons(SERV_PORT);
24
25     Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
26     Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
27
28     Listen(listenfd, LISTENQ);
29
30         /* 4create UDP socket */
31     udpfd = Socket(AF_INET, SOCK_DGRAM, 0);
32
33     bzero(&servaddr, sizeof(servaddr));
34     servaddr.sin_family      = AF_INET;
35     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
36     servaddr.sin_port        = htons(SERV_PORT);
37
38     Bind(udpfd, (SA *) &servaddr, sizeof(servaddr));
39 /* end udpservselect01 */
40
41 /* include udpservselect02 */
42     Signal(SIGCHLD, sig_chld);    /* must call waitpid() */
43
44     FD_ZERO(&rset);
45     maxfdp1 = max(listenfd, udpfd) + 1;
46     for ( ; ; ) {
47         FD_SET(listenfd, &rset);
48         FD_SET(udpfd, &rset);
49         if ( (nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0) {
50             if (errno == EINTR)
51                 continue;        /* back to for() */
52             else
53                 err_sys("select error");
54         }
55
56         if (FD_ISSET(listenfd, &rset)) {
57             len = sizeof(cliaddr);
58             connfd = Accept(listenfd, (SA *) &cliaddr, &len);
59
60             if ( (childpid = Fork()) == 0) {    /* child process */
61                 Close(listenfd);    /* close listening socket */
62                 str_echo(connfd);    /* process the request */
63                 exit(0);
64             }
65             Close(connfd);            /* parent closes connected socket */
66         }
67
68         if (FD_ISSET(udpfd, &rset)) {
69             len = sizeof(cliaddr);
70             n = Recvfrom(udpfd, mesg, MAXLINE, 0, (SA *) &cliaddr, &len);
71
72             Sendto(udpfd, mesg, n, 0, (SA *) &cliaddr, len);
73         }
74     }
75 }
76 /* end udpservselect02 */

sig_chld信号处理函数

 1 Signal(SIGCHLD,sig_chld);
 2
 3
 4 #include    "unp.h"
 5
 6 void
 7 sig_chld(int signo)
 8 {
 9     pid_t    pid;
10     int        stat;
11
12     pid = wait(&stat);
13     printf("child %d terminated\n", pid);
14     return;
15 }

时间: 2024-08-13 14:31:17

UNP学习笔记(第八章 基本UDP套接字编程)的相关文章

apue和unp的学习之旅10——基本udp套接字编程

使用UDP编写的一些常见的应用程序有:DNS(域名系统),NFS(网络文件系统),SNMP(简单网络管理协议). //---------------------------------1.recvfrom函数和sendto函数---------------------------------- #include <sys/socket.h> ssize_t  recvfrom(int sockfd, void* buff, size_t nbytes, int flags, struct so

【Python网络编程】利用Python进行TCP、UDP套接字编程

之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接字将该行发送到服务器. 2.服务器从其连接套接字读取一行字符. 3.服务器将该行字符转换成大写. 4.服务器将修改后的字符串(行)通过连接套接字再发回给客户机. 5.客户机从其套接字中读取修改后的行,然后将该行在其标准输出(监视器)上打印出来. [TCP]服务器端代码: import socket

《网络编程》基本 UDP 套接字编程

在前面文章中介绍了<UDP 协议>和<套接字数据传输>.UDP 协议和 TCP 协议不同,它是一种面向无连接.不可靠的传输层协议.在基于 UDP 套接字编程中,数据传输可用函数 sendto 和 recvfrom.以下是基本 UDP 套接字编程过程: sendto 与 recvfrom 函数 这两个函数的功能类似于 write 和 read 函数,可用无连接的套接字编程.其定义如下: /* 函数功能:发送数据: * 返回值:若成功则返回已发送的字节数,若出错则返回-1: * 函数原

探索UDP套接字编程

UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务,而TCP提供面向流.提供可靠数据服务.注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了. 典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等:服务端不接收来自客户端的连接,而只是调用recvfr

【转】 探索UDP套接字编程

UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务,而TCP提供面向流.提供可靠数据服务.注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了. 典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等:服务端不接收来自客户端的连接,而只是调用recvfr

linux下的UDP套接字编程

一.相关过程以及知识请详见我的另一篇博客<winsock套接字编程>,这里不再累述. 二.相关代码: server.c:  1 /****************************************                                                                                                    2     > File Name:server.c   3     > A

《网络编程》高级 UDP 套接字编程

概述 UDP 是一个无连接.不可靠的数据报协议,任何可靠传输都需由应用程序提供,例如:超时重传.序列号应答机制,但是它在某些场合使用效率高,方便.它支持广播和多播.有关<基本 UDP 套接字编程>参照该文,这里只是在那个基础上,记录一些在 UDP 编程中容易出现的问题. 辅助数据 辅助数据(也称为控制信息)可通过调用 recvmsg 和 sendmsg 函数使用,这里两个函数的定义可参考文章<高级 I/O>,使用 msghdr 结构体中的 msg_control 和 msg_con

TCP和UDP套接字编程 (java实现)

在了解网络编程之前,我们先了解一下什么叫套接字 套接字即指同一台主机内应用层和运输层之间的接口 由于这个套接字是建立在网络上建立网络应用的可编程接口 因此也将套接字称为应用程序和网络之间的应用程序编程接口! 关于TCP和UDP这里就不作太多介绍了,我们知道TCP是面向连接的,UDP是不面向连接的,TCP可靠,UDP不可靠即可! 我们来设计一个应用来示范一下,流程: 客户机从键盘读取一行字符串,并通过套接字发送到服务器. 服务器从连接的套接字获取这行字符串,并对其进行修改(将小写转为大写),最后再

Unix网络编程学习笔记之第8章 基于UDP套接字编程

一. UDP C/S的典型函数调用 UDP没有像TCP那样的连接,客户端直接sendto向某服务器发送数据,服务器端一直recvfrom阻塞,以接收任何客户端发送的数据. 二. sendto和recvfrom函数 int sendto(int sockfd, const void* buff, size_t nbytes, int flag, const struct sockaddr* to, socklen_taddrlen); int recvfrom(int sockfd, void*