基于UDP协议的接收和发送实验

本文介绍了基于UDP协议的收发实验,包括UDP服务器和客户端的编程框架,常用函数如socket(), bind(), recvfrom(), sendto()等。通过实验,实现了UDP echo服务,展示了获取服务器目录、文件上传和下载的功能。实验中强调了规范编程和避免并发进程冲突的重要性。" 120355258,7549875,算法每日一题:从基础到进阶,"['算法', '数据结构', '面试准备']
摘要由CSDN通过智能技术生成

基于UDP协议的接收和发送实验

选修了一门TCP/IP协议分析及应用,做了几个小实验,这里把实验的原理、自己的代码和经验教训和大家分享一下。

实验中的全部代码托管在Github上,请通过 fork + pull request 方法来帮助改进项目。

导语:

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);

建立一个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);              //服务器端口

地址结构的类型为AF_INEF ; IP地址为任意的本地地址; 服务器的端口为用户定义的端口地址; 注意成员sin_addr.s_addrsin_port均为网络字节序。
3. 绑定侦听端口,使用bind()函数,将套接字文件描述符和一个地址类型变量进行绑定,例如

bind(s,(struct sockaddr*) &addr_serv,sizeof(addr_serv));    //绑定地址
  1. 接收客户端的数据,使用recvfrom()函数接受客户端的网络数据。
  2. 向客户端发送数据,是应用sendto()函数向服务器主机发送数据。
  3. 关闭套接字,使用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);

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));      //套接字绑定

绑定函数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)

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);

四,实验代码实现

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"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值