1 ttp_open_transfer 函数 作用:(主要用来交互一些c/s 两端之间的通信协议)
服务器端接收客户端发来的所要请求的文件的名字 , 若是成功则返回0 , 否则返回< 0 。
(1) 服务器首先读取客户端发送来的请求文件名,然后对文件名进行判断,根据文件名的不同采取不同的操作。
a) 若文件名等于 "!#DIR??"
则表示客户端向 服务器请求了一系列的(已经共享的) 文件名 和 对应文件的大小。
于是 首先 server 首先将存储在 parameter结构体中的 total_files (表示文件的个数)传递给client 。
然后分别发送parameter结构体中的 file_names 和 file_sizes 。 这就将一些列文件的名字个大小发送给了服务器。
最后: 接收客户端发送而来的一个消息,确认客户端已经接收到了上述信息。
b) 若文件名是 "*"
/*表示客户端请求了多个文件 ,向客户端发送所有的文件的大小,然后是所有文件的个数*/
上面的两个变量存储在parameter 结构体中的 file_name_size 和 file_no 中。
然后server 接收 client发送过来的一个 message 。
下一步 服 务器 依次向 客户端发送 文件名字 。
然后server 再次接收 client发送过来的一个 message , 当做反馈。
接着 , server 试着 读取客户端发送来的请求一个特定文件的文件名。
(2)
进行完上面的判断之后,将得到的文件名,存储在 transfer 结构体中的变量filename ,
然后调用open函数 打开文件,将文件句柄存储在transfer的 file 变量中。
如果open函数失败那么就向客户端发送一个 内容为 0x008的 消息。
(3) 开始进行消息传递的估计
初始化 计时器 ping_s 。
向client 发送一个内容为000 的消息 。
(4) server 端接收 客户端发送来的 block_size , target_rate , error_rate
(5) 获得消息传递结束的时间 ping_e
(6) server 端接收 client 发来的 关于 speedup 和 slowdown 的一些参数 。
这些参数包括 :slower_num slower_den faster_num faster_den
(7) 得到transfer结构体中的 file_size 参数
fseeko(xfer->file , 0 , SEEK_END) ;
param->file_size = ftello(xfer->file) ;
fseeko(xfer->file , 0 , SEEK_SET) ;
以上3句,用来求出总的文件的大小。
(8)初始化 parameter结构体中的block_count 以及 epoch 两个变量 。
(9)将Parameter 变量中的 file_size block_size block_count epoch 传递给客户端 。
(10) 根据 ping_e ping_s 来初始化 wait_u_sec 。
然后 wait_u_sec 再增加自身的10% 。
(11) 然后求出 param->ipd_time , 以及 transfer的 ipd_current 。
(12)将一些信息写入日志中。
综上 : 这个函数主要是c/s 之间,来初始化parameter 结构体中的一些变量,如 file file_size , 以及一些file_size , block_size , block_count
epoch , 以及 wait_u_sec 等等 , 主要是一些消息的头部信息。
另外还有一点也显示出来作者的牛逼, 就是注意到了套接字设置为阻塞式 和 非阻塞 两种
在应用TCP协议协商头部信息的时候,使用的是阻塞式 , 而使用udp协议传递文件数据的时候,使用的是非阻塞式 。
2 下面附上client_handler 函数的剩余代码进行研究
/* while we haven't been told to stop */
/*直到通知结束的时候,才停止接收*/
while (1) {
/* make the client descriptor blocking */
/*将连接变为阻塞式链接*/
status = fcntl(session->client_fd, F_SETFL, 0);
if (status < 0)
error("Could not make client socket blocking");
/* negotiate another transfer */
status = ttp_open_transfer(session);
if (status < 0) {
warn("Invalid file request");
continue;
}
/* negotiate a data transfer port */
status = ttp_open_port(session);
if (status < 0) {
warn("UDP socket creation failed");
continue;
}
/* make the client descriptor non-blocking again */
/*将socket连接变为非阻塞式*/
status = fcntl(session->client_fd, F_SETFL, O_NONBLOCK);
if (status < 0)
error("Could not make client socket non-blocking");
/*---------------------------
* START TIMIN
* 开始计时
*---------------------------*/
gettimeofday(&start, NULL);
if (param->transcript_yn)
xscript_data_start(session, &start);
lasthblostreport = start;
lastfeedback = start;
prevpacketT = start;
deadconnection_counter = 0;
ipd_time = 0;
ipd_time_max = 0;
ipd_usleep_diff = 0;
retransmitlen = 0;
/* start by blasting out every block */
xfer->block = 0;
/*当当前transfer中的要传递的block index 比 paramter协商中的小,则一直传递*/
while (xfer->block <= param->block_count) {
/* default: flag as retransmitted block */
block_type = TS_BLOCK_RETRANSMISSION;
/* precalculate time to wait after sending the next packet */
gettimeofday(&currpacketT, NULL);
ipd_usleep_diff = xfer->ipd_current + tv_diff_usec(prevpacketT, currpacketT);
prevpacketT = currpacketT;
if (ipd_usleep_diff > 0 || ipd_time > 0) {
ipd_time += ipd_usleep_diff;
}
ipd_time_max = (ipd_time > ipd_time_max) ? ipd_time : ipd_time_max;
/* see if transmit requests are available */
/*从客户端的retransmission队列中接受一个消息*/
status = read(session->client_fd, ((char*)&retransmission)+retransmitlen, sizeof(retransmission)-retransmitlen);
#ifndef VSIB_REALTIME
if ((status <= 0) && (errno != EAGAIN))
error("Retransmission read failed");
#else
if ((status <= 0) && (errno != EAGAIN) && (!session->parameter->fileout))
error("Retransmission read failed and not writing local backup file");
#endif
if (status > 0)
retransmitlen += status;
/* if we have a retransmission */
/*当我们完整地接收了一个retransmission消息的时候*/
if (retransmitlen == sizeof(retransmission_t)) {
/* store current time */
lastfeedback = currpacketT;
lasthblostreport = currpacketT;
deadconnection_counter = 0;
/* if it's a stop request, go back to waiting for a filename */
if (ntohs(retransmission.request_type) == REQUEST_STOP) {
fprintf(stderr, "Transmission complete.\n");
break;
}
/* otherwise, handle the retransmission */
/*执行具体的消息传输操作*/
status = ttp_accept_retransmit(session, &retransmission, datagram);
if (status < 0)
warn("Retransmission error");
retransmitlen = 0;
/* if we have no retransmission */
/*如果当前接收的不是retransmission*/
} else if (retransmitlen < sizeof(retransmission_t)) {
/* build the block */
/*可能是因为transfer中的 block 已经传递到了最后一个*/
xfer->block = min(xfer->block + 1, param->block_count);
/*如果传递的是最后一个,那么就将消息类型变为TS_BLOCK_TERMINATE*/
block_type = (xfer->block == param->block_count) ? TS_BLOCK_TERMINATE : TS_BLOCK_ORIGINAL;
/*调用build_datagram函数,创建terminate消息*/
status = build_datagram(session, xfer->block, block_type, datagram);
if (status < 0) {
sprintf(g_error, "Could not read block #%u", xfer->block);
error(g_error);
}
/* transmit the block */
/*发送上面的消息 终止消息 或则 original 消息*/
status = sendto(xfer->udp_fd, datagram, 6 + param->block_size, 0, xfer->udp_address, xfer->udp_length);
if (status < 0) {
sprintf(g_error, "Could not transmit block #%u", xfer->block);
warn(g_error);
continue;
}
/* if we have too long retransmission message */
/*如果接收消息的长度大于了transmission 的长度,那么直接报错*/
} else if (retransmitlen > sizeof(retransmission_t)) {
fprintf(stderr, "warn: retransmitlen > %d\n", (int)sizeof(retransmission_t));
retransmitlen = 0;
}
/* monitor client heartbeat and disconnect dead client */
/*统计已经失去连接的client数*/
if ((deadconnection_counter++) > 2048) {
char stats_line[160];
deadconnection_counter = 0;
/* limit 'heartbeat lost' reports to 500ms intervals */
if (get_usec_since(&lasthblostreport) < 500000.0) continue;
gettimeofday(&lasthblostreport, NULL);
/* throttle IPD with fake 100% loss report */
#ifndef VSIB_REALTIME
/*发送REQUEST_ERROR_RATE消息*/
retransmission.request_type = htons(REQUEST_ERROR_RATE);
retransmission.error_rate = htonl(100000);
retransmission.block = 0;
ttp_accept_retransmit(session, &retransmission, datagram);
#endif
delta = get_usec_since(&lastfeedback);
/* show an (additional) statistics line */
snprintf(stats_line, sizeof(stats_line)-1,
" n/a n/a n/a %7u %6.2f %3u -- no heartbeat since %3.2fs\n",
xfer->block, 100.0 * xfer->block / param->block_count, session->session_id,
1e-6*delta);
if (param->transcript_yn)
xscript_data_log(session, stats_line);
fprintf(stderr, "%s", stats_line);
/* handle timeout for normal file transfers */
#ifndef VSIB_REALTIME
if ((1e-6 * delta) > param->hb_timeout) {
fprintf(stderr, "Heartbeat timeout of %d seconds reached, terminating transfer.\n", param->hb_timeout);
break;
}
#else
/* handle timeout condition for : realtime with local backup, simple realtime */
if ((1e-6 * delta) > param->hb_timeout) {
if ((session->parameter->fileout) && (block_type == TS_BLOCK_TERMINATE)) {
fprintf(stderr, "Reached the Terminate block and timed out, terminating transfer.\n");
break;
} else if(!session->parameter->fileout) {
fprintf(stderr, "Heartbeat timeout of %d seconds reached and not doing local backup, terminating transfer now.\n", param->hb_timeout);
break;
} else {
lastfeedback = currpacketT;
}
}
#endif
}
/* wait before handling the next packet */
if (block_type == TS_BLOCK_TERMINATE) {
usleep_that_works(10*ipd_time_max);
}
if (ipd_time > 0) {
usleep_that_works(ipd_time);
}
}
/*---------------------------
* STOP TIMING
*---------------------------*/
/*停止计时操作*/
gettimeofday(&stop, NULL);
if (param->transcript_yn)
xscript_data_stop(session, &stop);
delta = 1000000LL * (stop.tv_sec - start.tv_sec) + stop.tv_usec - start.tv_usec;
/* report on the transfer */
if (param->verbose_yn)
fprintf(stderr, "Server %d transferred %llu bytes in %0.2f seconds (%0.1f Mbps)\n",
session->session_id, (ull_t)param->file_size, delta / 1000000.0,
8.0 * param->file_size / (delta * 1e-6 * 1024*1024) );
/* close the transcript */
if (param->transcript_yn)
xscript_close(session, delta);
#ifndef VSIB_REALTIME
/* close the file */
fclose(xfer->file);
#else
/* VSIB local disk copy: close file only if file output was requested */
if (param->fileout) {
fclose(xfer->file);
}
/* stop the VSIB */
stop_vsib(session);
fclose(xfer->vsib);
#endif
/* close the UDP socket */
close(xfer->udp_fd);
memset(xfer, 0, sizeof(*xfer));
} //while(1)