P2P技术实现方式有很多,现主要介绍利用socket tcp方式构建P2P环境的方法。
在开始的时候主要参考NBD协议部分的server端程序来实现BT服务端程序。
每个client端都有两个进程来实现P2P功能,一个是客户端的server程序,另一个是客户端的client程序。而BT服务器端只有一个进程,负责资源分配和负载平衡等工作。
下面是服务端程序的注意事项。调试是总发现有“Address already in use”信息出现,加入下面代码可以解决此问题。
/* lose the pesky "Address already in use" error message */
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
perror("setsockopt SO_REUSEADDR");
}
if (setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
perror("setsockopt SO_KEEPALIVE");
}
每一个客户端发起请求时,首先询问服务器端,整个下载的镜像的种子分布状态,之后获得每一个片的种子分布状态。服务器端的server为每一个客户端的请求fork出一个进程。等待客户端下载完成后,报告个服务器端,此片我已经下载完成,并更新BT资源数据。同时可以设计客户端向BT服务器是否可以关机/开机做种等机制。
种子资源数据结构如下:
struct pieces_t{
struct list_head head;
struct pieceinfo_t pieceinfo;
u32 len; //how many clients connect the piece
} __attribute__ ((packed));
struct clientinfo_t {
struct list_head list;
char ipaddr[IPADDRLEN];
char mac[MACADDRLEN]; //MAC address
};
struct pieceinfo_t
{
u32 fid;
u64 offset;
u32 size;
int hash;
};
协议单元的程序例程如下:(客户端报告下载完成)
int download_finish(int sock,u32 fid)
{
struct smc_request request;
struct smc_reply reply;
int error = 0;
request.magic = htonl(SMC_REQUEST_MAGIC);
request.type = htonl(REQ_DOWNLOAD_FINISH);
request.fid = htonl(fid); // 0 means all pieces is downloaded
if(tcp_xmit(TCP_WRITE, sock, (char *)&request, sizeof(request)) <= 0) {
fprintf(stderr,"request_update_finish: failed 1/n");
error=-1;
goto err_out;
}
if(tcp_xmit(TCP_READ, sock, (char *)&reply, sizeof(reply)) <= 0) {
fprintf(stderr,"request_update_finish: failed 2/n");
error=-2;
goto err_out;
}
if(reply.error) {
error=-3;
fprintf(stderr,"request_update_finish: failed 3/n");
goto err_out;
}
DEBUG2("DOWNLOAD fid(%d) Finished/n",fid);
err_out:
return error;
}
同时设计了支持黑名单的功能。即当很多客户机(已经下载完毕)关机的情况下,正在下载的客户机避免一些不必要的连接损耗,提供如果connect时返回无法连接,直接把该ip对应的客户机放入blacklist链表中,下次下载时,如果该机器在blacklist中,可以skip掉。同时定期更新黑名单里面的机器。如果里边的机器已经开机。那么可以从黑名单中解放出来,继续给其他的客户机做种。