网络编程作业——UDP传输

UDP 编程

要求

实验一

  • 客户端和服务端交互,客户端发送信息
  • 服务端显示信息来源并且将接收到的信息回传给发送方
  • 服务端使用多线程实现并发
  • 观察在服务端启动或者不启动时,客户端运行情况

实验二

  • 客户端向服务端发送文件名
  • 客户端想服务端传输文件内容
  • 双方关闭套接字
  • 注意收发速度对文件的传输结果的影响

分析

实验一

  • 首先服务端启动
    和tcp的服务端启动类似,不过不需要listen,
    只要创建socket并且绑定端口就行。

  • 然后就是recvfrom等待客户端连接

  • 一旦客户端发送数据过来了,就创建新线程,在新线程中创建一个新的socket,绑定一个新的未被占用的端口,然后给客户端返回信息。

    //创建一个新的socket
    newsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (newsocket == INVALID_SOCKET)
    {
        cout << "Create Transfer socket failed with error: " << WSAGetLastError() << endl;
        return -1;
    }
    sockaddr_in newaddr;
    newaddr.sin_family = AF_INET;
    newaddr.sin_addr.s_addr = inet_addr(IP_ADDR);
    newaddr.sin_addr.S_un.S_addr = INADDR_ANY;
    memset(newaddr.sin_zero, 0x00, 8);

    int notfirst = 0;
    int newaddr_len = sizeof(newaddr);
    //bind socket,一个新的端口
    iResult = bind(newsocket, (struct sockaddr *)&newaddr, newaddr_len);
    if (iResult != 0)
    {
        cout << "Bind socket failed: " << WSAGetLastError() << endl;
        closesocket(newsocket);
        return -1;
    }
  • 采用循环交互,只要客户端不发送“End”,双方的通信就不会结束

  • UDP是无连接的,所以无论在服务端是否开启的情况下,客户端都能进行数据发送,因为客户根本不在乎服务端是否接受。
    在服务端没有开启的情况下,客户端不能收到服务端的返回,所以会超时错误。

实验二

  • 整个交互的过程是客户端发送文件名,服务端接受到之后,开一个新线程,创建一个新的套接字,绑定一个新的端口来进行余下的文件传输,通过给客户端返回信息来通知新的端口信息。然后,客户端发送文件长度,然后是文件内容,文件发送和接受都是循环的,直到文件传输完全成功

  • 设置了超时时间,在文件传输过程中,一旦发生问题,服务端接收不到后续信息,超时之后自动退出

//设置超时时间
    int nTimeOver = 10000;
    setsockopt(TransSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeOver, sizeof(nTimeOver));
  • 客户端也设置了超时时间,如果在发送文件结束之后,如果超时时间内没有收到服务端的确认信息就自动退出
int nTimeover = 1000;
        setsockopt(ClientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeover, sizeof(nTimeover));
  • 关于传输时间控制,使用的是sleep()函数,在前后两次发送之间调用sleep()来降低传输速度,从而可以使得UDP报文的数据不会因为传输速度太快而淹没。
Sleep(100);

实验结果

实验一

服务端开启的时候
  • 客户端参数设置

  • 可以多次发送数据

    可以多次发送数据

  • 可以接受多个客户端的同时发送数据

服务端关闭的时候

当服务器被关闭的时候

客户端可以正常发送数据,因为UDP不需要建立连接
但是客户端是不可能接收到服务端的返回信息所以会显示错误代码10060,就是在超时时间内接受不到,停止阻塞。

实验二

短文件传输
  • 客户端参数设置

PS:
2.txt大小为94643个字节

  • 文件传输

    此时文件长度还不是很长,每次发送都能被服务端接收到,而且返回信息也是成功。
长文件传输
  • 客户端参数设置

PS:
3.pdf大小为183662个字节

  • 文件传输

可以从运行结果看出来,当文件过大的时候,容易有一部分的数据不能被接收,非常容易造成内容的丢失。
此时服务端会等到超时时间到了之后自动停止阻塞

  • 修改——控制发送速度
    在每次发送,开始下一次发送之前,调用sleep函数来降低发送速度,此时,整个文件都能被服务端正确接受。

思考总结

  1. 注意初始化,有时候没有初始化会造成很大的问题

  2. UDP发送端的发送频率如果过快,就会导致信息的淹没,接收方不能接收到完整信息

  3. UDP是不可靠传输,所以需要设置超时时间,不然容易导致程序一直等待

  4. 如果UDP发送速度过快,会导致数据的淹没,从而出错。

  5. 总的来说,可靠的传输TCP虽然设置稍微复杂,但是稳定程度,可靠程度高很多,编程的时候也不容易出现特别奇怪的问题,不可靠传输UDP,虽然看上去简单一些,但是实际编程中会遇到各种乱七八糟的问题,也更加难以调试,难以发现错误。

代码

实验一

Client

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <WinSock2.h>
#include <iostream>

#pragma comment (lib,"ws2_32.lib")
#define MAX_LEN 100000
using namespace std;



int main(int argc, char * argv[])
{
    WSADATA ws;
    int iResult;
    SOCKET ClientSocket = INVALID_SOCKET;
    struct sockaddr_in Server_addr;
    int Server_len;


    //检查参数个数
    if (argc != 3)
    {
        cout << "Need enter target IP and port !" << endl;
        return -1;
    }
    int PORT = atoi(argv[2]);


    //初始化 socket
    iResult = WSAStartup(MAKEWORD(2, 2), &ws);
    if (iResult != 0)
    {
        cout << "Initiate failed with error: " << GetLastError() << endl;
        WSACleanup();
        return -1;
    }

    //创建 socket
    ClientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (ClientSocket == INVALID_SOCKET)
    {
        cout << "Create Client socket failed with error: " << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }


    Server_addr.sin_family = AF_INET;
    Server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    Server_addr.sin_port = htons(PORT);
    memset(Server_addr.sin_zero, 0x00, 8);
    Server_len = sizeof(Server_addr);

    char data[MAX_LEN];
    cout << "########################################### Interactive Mode ############################################" << endl;
    while (TRUE)
    {
        cout << "Please enter the data to send! Enter \"End\" to close the Interactive mode" << endl;
        cin >> data;
        cout << endl;
        int len = strlen(data);
        iResult = sendto(ClientSocket, data, strlen(data), 0, (sockaddr *)&Server_addr, Server_len);
        if (iResult != strlen(data) && iResult != MAX_LEN)
        {
            cout << "Sending failed with error :" << WSAGetLastError() << endl;
            cout << "Client exit." << endl;
            closesocket(ClientSocket);
            WSACleanup();
            return -1;
        }
        else if (iResult == MAX_LEN)
        {
            cout << "Sending data buffer is already full!\n " << WSAGetLastError() << endl;

        }

        int Recv_Server_addr_len = 0;
        char recvdata[MAX_LEN];
        sockaddr_in confire_addr;

        int nTimeover = 1000;
        setsockopt(ClientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeover, sizeof(nTimeover));


        iResult = recvfrom(ClientSocket, recvdata, MAX_LEN, 0, (sockaddr
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值