前言

一百年没有更新博客了,都怪开学一堆杂活(x

那就顺手把实验报告转到这边吧owo

本实验为SZU原创实验,实验开发团队的老师和助教们都很有耐心。。大赞,环境没配好去群里问是秒回的

相关资料:

实验文档:计算机网络课程综合实验平台 (snrc.site)

一、实验介绍

该实验主要实现了一个服务端和一个客户端。其中,服务端利用缓冲区逐段向客户端发送多个视频片段,并在客户端逐段接收,并利用已经设计好的播放器,在网页中播放连续的视频。

二、实验过程

0. 环境搭建

实验文件中包含了压缩包exp1-lib.zip,exp-code.zip,cne.zip

将其解压,并把前两个压缩包(exp1-lib.zip和exp-code.zip)解压后的文件置于一个目录下,于cne.zip解压出来的文件夹devcpp6中的devcppPortable.exe中打开client.c和server.c文件,选择编译环境为Computer-networks

devenvir.png

1. 单文件视频传输

阅读代码发现实验文件中已经实现了:

1)服务端的初始化阶段,包括创建套接字,绑定套接字和监听套接字,以及接收客户端下载请求和发送文件大小给客户端,发送文件结束符和关闭文件。

2)客户端的初始化阶段,向服务器请求文件并接受文件的大小,检查文件结束符后将接收到的视频写入文件中

我们需要实现的是:

1)在服务端:

将文件的内容循环读取到缓冲区中,再将缓冲区的内容发送给客户端,注意读到文件末尾可能无法完整填充整个缓冲区,需要做特判

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     // 发送视频片段
int send_count = 0;
while (send_count < file_size)
{
/***************************************************************************************************/
/************************** 任务1: 发送指定视频文件(按照固定大小buffer方式) ********************/
/***************************************************************************************************/
char buffer[BUFFER_SIZE] = {0};

int bytes_read = (send_count + BUFFER_SIZE <= file_size) ? BUFFER_SIZE : file_size - send_count;
fread(buffer, bytes_read, 1, fp);

bytes_sent = send(new_socket, buffer, bytes_read, 0);
if (bytes_sent < 0)
printf("ERROR in send video\n");

send_count += bytes_sent;
printf("video send progress: %d / %d\n", send_count, file_size);
}

2)在客户端:

接收服务端发送的数据并写入文件。文件末尾无法完整填充缓冲区的处理方式同服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
   int recv_count = 0;

while (recv_count < file_size)
{
/************************************************************************/
/***************** 任务3 : 如何使用buffer接收视频文件?*****************/
/************************************************************************/

bytes_recv = (recv_count + BUFFER_SIZE <= file_size) ? BUFFER_SIZE : file_size - recv_count;
bytes_recv = recv(sock, video_segement + recv_count, bytes_recv, 0);

recv_count += bytes_recv;
printf("%s recv progress: %d / %d\n", req, recv_count, file_size);
}

2. 实现流式视频传输

实现流式视频传输要求我们在客户端请求同一个视频的多个视频段,那就利用sprintf将指定视频名(包括视频名称-视频分辨率-视频码率)与视频段序号拼接在一起,在客户端循环请求并接收,在服务端循环发送。最后再在客户端调用player.h中设计好的函数,在网页中播放视频

在客户端:

调用StartStreamingServer() 初始化播放器等资源,在循环中利用sprintf构造请求的文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
StartStreamingServer();

for (int id = 0;id < 60; ++id){

// 发送下载请求
int bytes_sent = 0;
unsigned char s_stop_byte = 0xFF;

// 视频文件名
char req[REQUEST_SIZE] = "";
/******************************************************************/
/***************** 任务2:如何按顺序选择视频文件?*****************/
/******************************************************************/
sprintf(req, "ocean-1080p-8000k-%d.ts", id);

bytes_sent = send(sock, req, REQUEST_SIZE, 0);
if (bytes_sent < 0)
printf("ERROR in send\n");
bytes_sent = send(sock, &s_stop_byte, sizeof(s_stop_byte), 0);
if (bytes_sent < 0)
printf("ERROR in send\n");
printf("send req: %s\n", req);

接收完成时调用ReceiveSegment()将完整的视频段交付给播放器,等待播放
在结束阶段调用WaitEnd()等待视频播放完毕后关闭播放器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /***数据接收完成阶段***/
ReceiveSegment(video_segement, req, file_size);

// 释放内存
free(video_segement);
video_segement = NULL;
}
/*************************************************************************************/
/*********任务2(扩展):如何在视频流传输完成后,通知server结束视频传输?*************/
/*************************************************************************************/

/***结束阶段***/
closesocket(sock);
WSACleanup();

WaitEnd();
}

三、实验结果

先编译运行server.c,服务端启动监听,如下

1
2
3
Create Server Socket Success.
Server Bind Port Success.
Server Listening.....

后编译运行client.c,开始依次传输视频文件

1
2
3
4
5
6
ocean-1080p-8000k-59.ts recv progress: 178176 / 181608
ocean-1080p-8000k-59.ts recv progress: 179200 / 181608
ocean-1080p-8000k-59.ts recv progress: 180224 / 181608
ocean-1080p-8000k-59.ts recv progress: 181248 / *181608*
ocean-1080p-8000k-59.ts recv progress: 181608 / 181608
file_path .\download\ocean-1080p-8000k-59.ts

网页播放视频如下:

streamingplay.png

帅帅帅

(其实还有思考题,找时间再研究下