bittorrent 学习(二) LOG日志和peer管理连接
代码中的log.h log.c比较简单
void logcmd() 记录命令 int logfile();运行日志的记录
int init_logfile() 开启log文件
源码比较清晰也很简单。 可以直接看代码
//=====================================================================================
peer代码中 我们先来看看结构体
typedef struct _Request_piece {
int index; // 请求的piece的索引 也是bitmap中的index??
int begin; // 请求的piece的偏移
int length; // 请求的长度,一般为16KB
struct _Request_piece *next;
} Request_piece;
typedef struct _Peer {
int socket; // 通过该socket与peer进行通信
char ip[]; // peer的ip地址
unsigned short port; // peer的端口号
char id[]; // peer的id
int state; // 当前所处的状态
int am_choking; // 是否将peer阻塞
int am_interested; // 是否对peer感兴趣
int peer_choking; // 是否被peer阻塞
int peer_interested; // 是否被peer感兴趣
Bitmap bitmap; // 存放peer的位图
char *in_buff; // 存放从peer处获取的消息
int buff_len; // 缓存区in_buff的长度
char *out_msg; // 存放将发送给peer的消息
int msg_len; // 缓冲区out_msg的长度
char *out_msg_copy; // out_msg的副本,发送时使用该缓冲区
int msg_copy_len; // 缓冲区out_msg_copy的长度
int msg_copy_index; // 下一次要发送的数据的偏移量
Request_piece *Request_piece_head; // 向peer请求数据的队列
Request_piece *Requested_piece_head; // 被peer请求数据的队列
unsigned int down_total; // 从该peer下载的数据的总和
unsigned int up_total; // 向该peer上传的数据的总和
time_t start_timestamp; // 最近一次接收到peer消息的时间
time_t recet_timestamp; // 最近一次发送消息给peer的时间
time_t last_down_timestamp; // 最近下载数据的开始时间
time_t last_up_timestamp; // 最近上传数据的开始时间
long long down_count; // 本计时周期从peer下载的数据的字节数
long long up_count; // 本计时周期向peer上传的数据的字节数
float down_rate; // 本计时周期从peer处下载数据的速度
float up_rate; // 本计时周期向peer处上传数据的速度
struct _Peer *next; // 指向下一个Peer结构体
} Peer;
注释标注的很清晰。 注意一点的是Peer和 Request_piece都是链表形式
Peer结构体体中 int state; // 当前所处的状态
状态定义为以下几种
#define INITIAL -1 // 表明处于初始化状态
#define HALFSHAKED 0 // 表明处于半握手状态
#define HANDSHAKED 1 // 表明处于全握手状态
#define SENDBITFIELD 2 // 表明处于已发送位图状态
#define RECVBITFIELD 3 // 表明处于已接收位图状态
#define DATA 4 // 表明处于与peer交换数据的状态
#define CLOSING 5 // 表明处于即将与peer断开的状态

但是状态的切换是不在Peer.c这个代码中, 真正的代码切换是在流程处理中,后面其他代码会慢慢讲到
先来看看Peer的初始化
int initialize_peer(Peer *peer)
{
if(peer == NULL) return -; peer->socket = -;
memset(peer->ip,,);
peer->port = ;
memset(peer->id,,);
peer->state = INITIAL; peer->in_buff = NULL;
peer->out_msg = NULL;
peer->out_msg_copy = NULL; peer->in_buff = (char *)malloc(MSG_SIZE);
if(peer->in_buff == NULL) goto OUT;
memset(peer->in_buff,,MSG_SIZE);
peer->buff_len = ; peer->out_msg = (char *)malloc(MSG_SIZE);
if(peer->out_msg == NULL) goto OUT;
memset(peer->out_msg,,MSG_SIZE);
peer->msg_len = ; peer->out_msg_copy = (char *)malloc(MSG_SIZE);
if(peer->out_msg_copy == NULL) goto OUT;
memset(peer->out_msg_copy,,MSG_SIZE);
peer->msg_copy_len = ;
peer->msg_copy_index = ; peer->am_choking = ;
peer->am_interested = ;
peer->peer_choking = ;
peer->peer_interested = ; peer->bitmap.bitfield = NULL;
peer->bitmap.bitfield_length = ;
peer->bitmap.valid_length = ; peer->Request_piece_head = NULL;
peer->Requested_piece_head = NULL; peer->down_total = ;
peer->up_total = ; peer->start_timestamp = ;
peer->recet_timestamp = ; peer->last_down_timestamp = ;
peer->last_up_timestamp = ;
peer->down_count = ;
peer->up_count = ;
peer->down_rate = 0.0;
peer->up_rate = 0.0; peer->next = (Peer *);
return ; OUT:
if(peer->in_buff != NULL) free(peer->in_buff);
if(peer->out_msg != NULL) free(peer->out_msg);
if(peer->out_msg_copy != NULL) free(peer->out_msg_copy);
return -;
}
该创建的创建 该分配的分配 该置零的置零
这里使用了不常见的GOTO。为了保证逻辑清晰,一般是不允许代码里四处GOTO跳转的。
但是GOTO在跳出多重循环和 调至结尾释放资源是比较清晰简洁的写法。
GOTO可以避免多处return忘记释放资源,而是跳转到结尾释放资源后return。return值在处理流程中会赋值1或者-1 表示成功与否
这种写法在结构复杂,多出return还有资源要释放时可以尝试使用下.
其他函数比较简单
Peer* add_peer_node(); // 添加一个peer结点 插入链表
int del_peer_node(Peer *peer); // 从链表中删除一个peer结点
void free_peer_node(Peer *node); // 释放一个peer的内存
int cancel_request_list(Peer *node); // 撤消当前请求队列
int cancel_requested_list(Peer *node); // 撤消当前被请求队列
void release_memory_in_peer(); // 释放peer.c中的动态分配的内存
void print_peers_data(); // 打印peer链表中某些成员的值,用于调试
Peer* add_peer_node()
{
int ret;
Peer *node, *p; // 分配内存空间
node = (Peer *)malloc(sizeof(Peer));
if(node == NULL) {
printf("%s:%d error\n",__FILE__,__LINE__);
return NULL;
} // 进行初始化
ret = initialize_peer(node);
if(ret < ) {
printf("%s:%d error\n",__FILE__,__LINE__);
free(node);
return NULL;
} // 将node加入到peer链表中
if(peer_head == NULL) { peer_head = node; }
else {
p = peer_head;
while(p->next != NULL) p = p->next;
p->next = node;
} return node;
} int del_peer_node(Peer *peer)
{
Peer *p = peer_head, *q; if(peer == NULL) return -; while(p != NULL) {
if( p == peer ) {
if(p == peer_head) peer_head = p->next;
else q->next = p->next;
free_peer_node(p); // 可能存在问题
return ;
} else {
q = p;
p = p->next;
}
} return -;
} // 撤消当前请求队列
int cancel_request_list(Peer *node)
{
Request_piece *p; p = node->Request_piece_head;
while(p != NULL) {
node->Request_piece_head = node->Request_piece_head->next;
free(p);
p = node->Request_piece_head;
} return ;
} // 撤消当前被请求队列
int cancel_requested_list(Peer *node)
{
Request_piece *p; p = node->Requested_piece_head;
while(p != NULL) {
node->Requested_piece_head = node->Requested_piece_head->next;
free(p);
p = node->Requested_piece_head;
} return ;
} void free_peer_node(Peer *node)
{
if(node == NULL) return;
if(node->bitmap.bitfield != NULL) {
free(node->bitmap.bitfield);
node->bitmap.bitfield = NULL;
}
if(node->in_buff != NULL) {
free(node->in_buff);
node->in_buff = NULL;
}
if(node->out_msg != NULL) {
free(node->out_msg);
node->out_msg = NULL;
}
if(node->out_msg_copy != NULL) {
free(node->out_msg_copy);
node->out_msg_copy = NULL;
} cancel_request_list(node);
cancel_requested_list(node); // 释放完peer成员的内存后,再释放peer所占的内存
free(node);
} void release_memory_in_peer()
{
Peer *p; if(peer_head == NULL) return; p = peer_head;
while(p != NULL) {
peer_head = peer_head->next;
free_peer_node(p);
p = peer_head;
}
} void print_peers_data()
{
Peer *p = peer_head;
int index = ; while(p != NULL) {
printf("peer: %d down_rate: %.2f \n", index, p->down_rate); index++;
p = p->next;
}
}
都是常规链表操作
//=======================================================================
参考
《linux c编程实战》第十三章节btcorrent 及代码
bittorrent 学习(二) LOG日志和peer管理连接的更多相关文章
- 代码实现获取log日志和logcat使用方法
代码实现获取log日志new Thread(new Runnable() { @Override publi ...
- Linux学习之CentOS(二十六)--Linux磁盘管理:LVM逻辑卷的创建及使用
在上一篇随笔里面 Linux学习之CentOS(二十五)--Linux磁盘管理:LVM逻辑卷基本概念及LVM的工作原理,详细的讲解了Linux的动态磁盘管理LVM逻辑卷的基本概念以及LVM的工作原理, ...
- 安装程序配置服务器失败。参考服务器错误日志和C:\windows\sqlstp.log 了解更多信息
重装sql经常遇到2个问题 1,以前的某个程序安装已在安装计算机上创建挂起的文件操作.运行安装程序之前必须重新启动计算机. 删除C:\Program Files\Microsoft SQL Serve ...
- ReactJS入门学习二
ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...
- bittorrent 学习(一) 种子文件分析与bitmap位图
终于抽出时间来进行 BITTORRENT的学习了 BT想必大家都很熟悉了,是一种文件分发协议.每个下载者在下载的同时也在向其他下载者分享文件. 相对于FTP HTTP协议,BT并不是从某一个或者几个指 ...
- Android JNI学习(二)——实战JNI之“hello world”
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- 第三篇-分析日志和sensor-data中的数据结构
分析日志和sensor-data数据结构 该文章提供web端思路,ios和android端思路不提供,api也已经下线,本文也不提供任何可执行代码.有更多疑问欢迎查看github代码 协议 授权协议: ...
- Java开发学习(二十二)----Spring事务属性、事务传播行为
一.事务配置 上面这些属性都可以在@Transactional注解的参数上进行设置. readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true. timeout ...
- 我的MYSQL学习心得(十三) 权限管理
我的MYSQL学习心得(十三) 权限管理 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) ...
随机推荐
- IDL实现矢量文件裁剪栅格数据
利用ENVI_SUBSET_VIA_ROI_DOIT函数实现矢量文件裁剪栅格数据. 测试数据:栅格文件是Mercator投影,矢量文件是Geographic坐标系 infile='F:\Temp_Da ...
- 分布式 基本理论 CAP 2
关于P P, 即 Partition字面意思是网络分区,其实 包括了 各种网络问题, 我们要把它理解 一个 广义的 分区问题. P 涉及到了 时间, 这么说吧, 出现了分区, 那就是节点之间 “长久的 ...
- log4j2 Filter用法详解
主要说下组合过滤器 CompositeFilter ,比较常用 <Filters> 是组合过滤器额标签,它包含的子标签是具体的过滤器,这三个具体过滤器分别是日志等级过滤器,正则表达式过滤器 ...
- azkaban使用--依赖dependencies作业
1.创建作业 [root@localhost azkaban_job]# ls one.job two.job [root@localhost azkaban_job]# cat one.job ty ...
- [C#]打包项目[转]
原文:https://www.cnblogs.com/danyu/p/7243706.html 加入自定义操作:https://blog.csdn.net/ristal/article/details ...
- 'Settings' object has no attribute 'FYFQ_URL_test'
读取django settings内容时报错: 'Settings' object has no attribute 'FYFQ_URL_test' 原因:settings中的变量,必须都是大写
- <mvc:resources mapping="/xxx/**" location="/xxx/"/>无效,可能和Controller的URL模式有关
某项目webapp下有子目录res,其中有img.css.js等存放静态资源的文件夹. 在定义了dispacher-servlet的<url-pattern>/</url-patte ...
- 【转】如何用Eclispe调试java -jar xxx.jar 方式执行的jar包
原文地址:https://www.cnblogs.com/zzpbuaa/p/5443269.html 有时候,我们经常会需要调试 java -jar xxx.jar方式运行的代码,而不是必须在Ecl ...
- 使用IDEA的项目上传到GitHub
使用IDEA的项目上传到GitHub 前提条件: 1.配置好Git(https://jingyan.baidu.com/article/8275fc867b652046a03cf613.html) 2 ...
- PeopleSoft通过status汇总进程运行情况
下面SQL可以按照run_status汇总进程的运行状态 SELECT RQST.RUNSTATUS, RQST.PRCSTYPE, (SELECT XLAT.XLATLONGNAME FROM PS ...