- 导语
- 一实验内容
- 二UDP编程框架
- 三UDP协议程序设计的常用函数
- 四实验代码实现
- 五程序扩展实现
- 六程序运行结果
-
导语:
UDP协议是User Datagram Protocol 的简称,他是无连接的,不可靠的网络协议。
本实验目的是使用因特网提供的UDP传输协议,实现一个简单的UDP客户/服务器程序,以了解传输层所提供的UDP服务的特点,应用层和传输层之间的软件接口风格,熟悉socket机制和UDP客户端/服务器方式程序的结构。
本文在介绍UDP协议收发技术的同时,提供了相关代码,并将笔者在debug过程中的经验和教训带给读者。一,实验内容
设计与实现UDP echo客户及服务器程序,完成以下功能:
客户从标准输入读一行文本,写到服务器上;服务器从网络输入读取此行,并回射(echo)给客户;客户读此回射行,并将其写到标准输出。
扩展一下三个内容
* 在客户机上显示服务器的目录
* 将服务器指定的文件下载到客户机
* 讲客户机指定的文件上传到服务器二,UDP编程框架
使用UDP进行程序设计可以分为客户端和服务器端两个部分。服务器端主要包含建立套接字、将套接字与地址结构进行帮顶、读写数据、关闭套接字几个过程。客户端包括建立套接字、读写数据、关闭套接字几个过程。服务器端和客户端两个流程志坚的主要差别在于对地址的绑定(
bind()
)函数,客户端可以不用进行地址和端口的绑定操作。2.1 UDP服务器编程框架
上图中对UDP协议服务器程序框架进行了说明,服务器流程主要分为下述6个部分吗,即建立套接字、设置套接字地址参数、进行端口绑定、接收数据、发送数据、关闭套接字。
1. 建立套接字文件描述符,使用socket()
, 生成套接字文件描述符,例如:int s = socket(AF_INET, SOCK_DGRAM, 0);
- 1
建立一个
AF_INET
族的数据包套接字,UDP协议的套接字使用SOCK_DGRAM
选项。
2. 设置服务器地址和侦听端口,初始化要绑定的网络地址结构,例如:struct sockaddr addr_serv; addr_serv.sin_family = AF_INET; //地址类型为AF_INET addr_serv.sin.addr.s_addr = htonl(INADDR_ANY); //任意本机地址 addr_serv.sin_port = htons(PORT_SERV); //服务器端口
- 1
- 2
- 3
- 4
地址结构的类型为
AF_INEF
; IP地址为任意的本地地址; 服务器的端口为用户定义的端口地址; 注意成员sin_addr.s_addr
和sin_port
均为网络字节序。
3. 绑定侦听端口,使用bind()函数,将套接字文件描述符和一个地址类型变量进行绑定,例如bind(s,(struct sockaddr*) &addr_serv,sizeof(addr_serv)); //绑定地址
- 1
- 接收客户端的数据,使用
recvfrom()
函数接受客户端的网络数据。 - 向客户端发送数据,是应用
sendto()
函数向服务器主机发送数据。 - 关闭套接字,使用
close()
函数释放资源
2.2 UDP客户端编程框架
在上图中,同样对UDP协议的客户端流程进行了描述,按照图中所示,UDP协议的客户端流程分为套接字建立、设置目的的地址和端口、向服务器发送数据、从服务器接收数据、关闭套接字5个部分。与服务器的框架相比,少了
bind()
部分,客户端程序的端口和本地的地址可以由系统使用时指定,在使用sendto()
和recvfrom()
的时候,网络协议栈会临时指定本地的端口和地址,流程如下:
1. 建立套接字文件描述符,socket()
;
2. 设置服务器地址和端口,struct sockaddr
;
3. 向服务器地址和端口,sendto()
;
4. 接收服务器的数据,recvfrom()
;
5. 关闭套接字,close()
;三,UDP协议程序设计的常用函数
UDP协议常用的函数有
recv()
/recvform()
、send()
/sendto()
、socket()
、bind()
等。当然这些函数同样可以用于TCP协议的程序设计。3.1 建立套接字socket() 和绑定套接字bind()
UDP协议使用建立套接字的方式和TCP方式一样,使用
socket()
函数,只不过协议的类型描述符使用SOCK_DGRAM
, 而不是参数SOCK_STREAM
。 例如下面是一个建立UDP套接字文件描述符的代码。int s; s = socket(AF_INEF, SOCK_DGRAM, 0);
- 1
- 2
UDP协议使用
bind()
函数的方法与TCP没有什么差别,将一个套接字描述符与一个地址结构绑定在一起。例如,下面的代码将一个本地的地址和套接字文件描述符绑定在在了一起。struct sockaddr_in local; //本地的地址信息 int from_len = sizeof(from); //地址结构的长度 local. sin _family = AF_INET; //协议簇 local. sin _port = htons(8888); //本地端口 local. sin _addr.s_addr = htonl(INADDR_ANY); //任意本机地址 s = socket(AF_INET, SOCK_DGRAM, 0); //初始化一个IPv4族的数据包套接字 if (s == -1) { //检查是否正常初始化socket perror("socket"); exit(EXIT_FAILURE); } bind(s, (struct sockaddr*) &local, sizeof(local)); //套接字绑定
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
绑定函数
bind()
使用的时机,即什么时候需要绑定需要介绍一下。函数bind()
的作用是将一个套接字文件描述符和一个本地地址绑定在一起,即把发送数据的端口地址和IP地址进行了制定。例如在发送数据的时候,如果不进行绑定,则会临时选择一个随机的端口。3.2 接收函数recvfrom()/recv()
当客户端成功建立了一个套接字文件描述符并建立了合适的
strcut sockaddr
结构后,或者服务器端成功将套接字文件描述符和地址结构绑定后,可以使用recv()
故意整个recvfrom()
来接收到达此套接字文件描述符上的数据或者在这个套接字文件描述符上的等待数据的到来。recv()
函数和recvfrom()
函数的原型如下:#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int s, void *buff, size_t len, int flags); ssize_t recvfrom(int s, void *buff,size_t len, int flags, struct sockaddr *from, socklen_t *from)
- 1
- 2
- 3
- 4
3.3 发送函数sendto()/send()
当客户端成功建立了一个套接字文件描述符,并构建了合适的
struct sockaddr
结构后,或者服务器成功地将套接字文件描述符和地址结构绑定后,可以使用send()
或者sendto()
函数来发送数据到某个主机上。send()
函数和sendto()
函数的原型如下:#include <sys/types.h> #include <sys/socket.h> ssize_t send(int s, const void *buff, size_t len, int flags); ssize_t sento(int s, const void *buff, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
- 1
- 2
- 3
- 4
四,实验代码实现
4.1 UDP客户端
dupcli01.cpp
- 首先给出udp client端的代码,标准的实现方法,比较简单。
/* * udpcli01.cpp * * Created on: 2015年4月23日 * Author: gzxultra */ #include <netinet/in.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <arpa/inet.h> #define MAXLINE 4096 #define LISTENQ 1024 /* 2nd argument to listen() */ #define SERV_PORT 9877 #define SA struct sockaddr void dg_cli(FILE *, int, const SA *, socklen_t); int main(int argc, char **argv) { int sockfd; struct sockaddr_in servaddr; if (argc != 2) { printf("usage:udpcli01sigio <IPaddress>\n"); exit(1); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0) { printf("socket error.\n"); exit(1); } 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); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
/* * udpserv01.cpp * * Created on: 2015年4月23日 * Author: gzxultra */ #include <netinet/in.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #define MAXLINE 4096 #define LISTENQ 1024 /* 2nd argument to listen() */ #define SERV_PORT 9877 #define SA struct sockaddr #include <string.h> static int sockfd; void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg); void GetList(int sockfd, SA *pcliaddr, socklen_t clilen) ; int main(int argc, char ** argv) {
<span class="hljs-keyword">struct</span> sockaddr_in servaddr, cliaddr; <span class="hljs-comment">//使用socket()函数生成套接字文件描述符</span> <span class="hljs-keyword">if</span> ((sockfd = socket(AF_INET, SOCK_DGRAM, <span class="hljs-number">0</span>)) < <span class="hljs-number">0</span>) { <span class="hljs-built_in">printf</span>(<span class="hljs-string">"socket error.\n"</span>); <span class="hljs-built_in">exit</span>(<span class="hljs-number">1</span>); } <span class="hljs-comment">//设置服务器地址和侦听端口,初始化要绑定的网络地址结构</span> bzero(&servaddr, <span class="hljs-keyword">sizeof</span>(servaddr)); servaddr.sin_family = AF_INET; <span class="hljs-comment">//地址类型为AF_INET</span> servaddr.sin_addr.s_addr = htonl(INADDR_ANY); <span class="hljs-comment">//任意本地地址</span> servaddr.sin_port = htons(SERV_PORT); <span class="hljs-comment">//服务器端口</span> <span class="hljs-comment">//绑定侦听端口</span> bind(sockfd, (SA *) &servaddr, <span class="hljs-keyword">sizeof</span>(servaddr)); <span class="hljs-keyword">char</span> instruction[<span class="hljs-number">3</span>][<span class="hljs-number">10</span>] = { <span class="hljs-string">"getlist\n"</span>, <span class="hljs-string">"download\n"</span>, <span class="hljs-string">"upload\n"</span> }; <span class="hljs-keyword">int</span> op = <span class="hljs-number">0</span>; <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>,j = <span class="hljs-number">0</span>; <span class="hljs-keyword">int</span> n = <span class="hljs-number">0</span>; <span class="hljs-keyword">char</span> mesg[MAXLINE]; socklen_t len = <span class="hljs-keyword">sizeof</span>(cliaddr); <span class="hljs-comment">//printf("running here!\n");</span> <span class="hljs-keyword">while</span>(<span class="hljs-number">1</span>){ <span class="hljs-comment">//len = clilen;</span> <span class="hljs-built_in">memset</span>(mesg,<span class="hljs-number">0</span>,<span class="hljs-keyword">sizeof</span>(mesg)); <span class="hljs-comment">//recvfrom接收客户端的网络数据</span> n = recvfrom(sockfd, mesg, MAXLINE, <span class="hljs-number">0</span>, (SA *) &cliaddr, &len); op = <span class="hljs-number">0</span>; <span class="hljs-comment">//处理接受到的回车符</span> <span class="hljs-keyword">for</span>(j=<span class="hljs-number">0</span>;j<=n;j++){ <span class="hljs-keyword">if</span>(mesg[j] == <span class="hljs-string">'\n'</span>) mesg[j] = <span class="hljs-string">'\0'</span>; <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">for</span>(i = <span class="hljs-number">1</span>;i<=<span class="hljs-number">3</span>;i++) <span class="hljs-keyword">if</span>(<span class="hljs-built_in">strcmp</span>(mesg,instruction[i-<span class="hljs-number">1</span>])== <span class="hljs-number">0</span>){ op = i; <span class="hljs-keyword">break</span>; } <span class="hljs-comment">//choose operation</span> <span class="hljs-keyword">for</span>(j=<span class="hljs-number">0</span>;j<=n;j++){ <span class="hljs-keyword">if</span>(mesg[j] == <span class="hljs-string">'\0'</span>) mesg[j] = <span class="hljs-string">'\n'</span>; <span class="hljs-keyword">break</span>; } <span class="hljs-built_in">printf</span>(<span class="hljs-string">"op = %d\n"</span>,op); <span class="hljs-keyword">switch</span>(op){ <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>: GetList(sockfd, (SA *) &cliaddr, <span class="hljs-keyword">sizeof</span>(cliaddr)); <span class="hljs-keyword">break</span>; <span class="hljs-comment">//case 2:DownLoad();break;</span> <span class="hljs-comment">//case 3:UpLoad();break;</span> <span class="hljs-keyword">default</span>:{ dg_echo(sockfd, (SA *) &cliaddr, <span class="hljs-keyword">sizeof</span>(cliaddr), mesg); <span class="hljs-keyword">break</span>; } } <span class="hljs-comment">//dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr), mesg);</span> <span class="hljs-comment">//printf("echo done!\n");</span> }
}
void GetList(int sockfd, SA *pcliaddr, socklen_t clilen) {
int n;
socklen_t len;<span class="hljs-built_in">printf</span>(<span class="hljs-string">"catch\n"</span>); <span class="hljs-comment">//char mesg[MAXLINE];</span> <span class="hljs-keyword">char</span> strlin[<span class="hljs-number">100</span>][MAXLINE]; <span class="hljs-comment">//一百行缓冲区</span> <span class="hljs-keyword">char</span> sendline[MAXLINE]={}; <span class="hljs-comment">//最终发送的字符串</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; FILE *fp; system(<span class="hljs-string">"ls -l>filelog.txt"</span>); <span class="hljs-comment">//输出重定向</span> <span class="hljs-comment">//printf("2done!\n");</span> ssize_t read; <span class="hljs-comment">//char c;</span> <span class="hljs-comment">//int count=0;</span> fp = fopen(<span class="hljs-string">"filelog.txt"</span>, <span class="hljs-string">"r"</span>); <span class="hljs-keyword">if</span> (!fp) { <span class="hljs-built_in">printf</span>(<span class="hljs-string">"get ls order failed!"</span>); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">//printf("4done!\n");</span> <span class="hljs-comment">//printf("%p\n", fp);</span> <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>) { <span class="hljs-comment">//printf("2done!!!!\n");</span> <span class="hljs-keyword">if</span> (NULL==fgets(strlin[i], MAXLINE, fp)) <span class="hljs-keyword">break</span>; <span class="hljs-comment">//如果读到文件尾,结束读取</span> <span class="hljs-comment">//strlin[i][strlen(strlin[i] - 1)] = '\0'; //每行加\0结尾</span> i++; <span class="hljs-comment">//写成strlin[i++]提示i未定义,gcc的bug??</span> } <span class="hljs-comment">/* ++i; memcpy(strlin[i],"EOF",sizeof("EOF")); */</span> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> n = <span class="hljs-number">0</span>; n < i; n++){ <span class="hljs-built_in">strcat</span>(sendline, strlin[n]); <span class="hljs-comment">//strcat(sendline,"\n");</span> } <span class="hljs-comment">//sendto(sockfd, strlin, sizeof(strlin), 0, pcliaddr, len);</span> <span class="hljs-built_in">fputs</span>(sendline,stdout); sendto(sockfd, sendline, <span class="hljs-built_in">strlen</span>(sendline)+<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, pcliaddr, clilen); } <span class="hljs-comment">//strcat(sendline, '\0');</span> fclose(fp); <span class="hljs-built_in">printf</span>(<span class="hljs-string">"FileList Get!\n"</span>);
}
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg) {
//fputs(mesg,stdout);
//printf(“\n”);
sendto(sockfd, mesg, strlen(mesg)+1, 0, pcliaddr, clilen);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
UDP的代码实例 接收和发送
最新推荐文章于 2024-03-20 19:24:13 发布