alanmcgovern / monotorrent

The official repository for MonoTorrent, a bittorrent library for .NET
https://github.com/alanmcgovern/monotorrent
MIT License
1.14k stars 395 forks source link

A seeding client refuses to seed #627

Closed ManlyMarco closed 1 year ago

ManlyMarco commented 1 year ago

Looks like I've hit a new issue. The seeding client refuses to seed one of the auto generated torrents. There's no errors generating it nor adding it, and the tracker works fine. The peer connects to the seed, but the seed closes the connection immediately (tested both in a monotorrent client and uTorrent 2.2.1, in monotorrent the seed gets removed from the peers list immediately after it's added while uTorrent periodically tries to connect but the seed closes the connection immediately).

The torrent contains a very large amount of small files, it's 163KB with only V1 hashes. I'm guessing it's something about that. Other torrents generated in the same way seed fine. There's nothing in the log outside of this:

Client log:

[18:56:37] INFO: ConnectionManager:ipv4://xx.xx.xxx.xxx:15847/: Connection opened
[18:56:37] INFO: ConnectionManager:ipv4://xx.xx.xxx.xxx:15847/: [outgoing] Sending handshake message with peer id '-MO3000-374654087375'

Server log:

INFO: ListenManager:ipv4://yyy.yyy.yy.yyy:51952/: ConnectionReceived

Packet dump: stuck.zip

alanmcgovern commented 1 year ago

Hrm, i can't get tell from the wireshark dump as the only two messasges visible in it are the two handshakes (one going each way).

The peer connects to the seed, but the seed closes the connection immediately (tested both in a monotorrent client and uTorrent 2.2.1, in monotorrent the seed gets removed from the peers list immediately after it's added while uTorrent periodically tries to connect but the seed closes the connection immediately

Is this a private torrent? If so it's possible the connection is being closed due to the peer id reported to the tracker being different to the peer id received by the client which established the outgoing connection.

i.e. peer A connects to the tracker and receives a list of peers, including peer B and peer B's peer id. peer A connects to peer B peer A sends a handshake to peer B containing peer A's peer id peer B accepts this and responds with it's handshake , containing peer B's peer id. peer A closes the connection if peer B reported a different peer ID to the one it expected to receive.

This is something i'm going to address as part of https://github.com/alanmcgovern/monotorrent/pull/614#issuecomment-1412700393 , though if this were actually happening in your case it should show in the seeder/leecher logs (whichever client initiates the connection should log that it's closing the connection due to the mismatch).

ManlyMarco commented 1 year ago

The torrent was set to public for using AddPeer to test if manually adding the seed helps anything, so being private doesn't matter. It seems to happen more often the more files there are in the torrent, but it's not consistent at all. It happens the most often on a torrent with around 1200 files, most around 450KB (I'm using V1 only mode). I added extra logging to the server to try to find any sort of clue as to what is happening, but it decided to work correctly after the server restart. I'll post an update if I learn anything new.

ManlyMarco commented 1 year ago

It looks like no exceptions are thrown when this happens.

It seems that after restarting the server side app, the seed always works fine, but it stops working after either restarting the client and starting the download over a few times, or after letting the seed idle for a while, not sure which one. It could also just be luck, but once it stops working it stops working for good, maybe it's something related to open connections?

If you have any test builds I could try to find the issue, let me know.

Edit: This issue is constrained to a particular torrent - with multiple torrents being seeded if I manage to get the bug to happen it only happens for a single torrent (unless I manage to trigger it again with another torrent). When the bug is active, it looks like no one can download the bugged torrent from the seed, even if it's their first time connecting (they can download other non-bugged torrents just fine).

Edit2: Could this be related to the max open file limit? I just noticed I had it set to 200 while the torrents easily surpassed that. I'll try bumping the value up and see what happens. When testing I was using only one client, downloading one torrent (but I had all torrents added to the client and started to seed them, so the seed shouldn't be affected by that).

alanmcgovern commented 1 year ago

@ManlyMarco By any chance is TorrentManager.State set to TorrentState.Error for the TorrentManager which bugged? If so, what's the value stored in TorrentManager.Error ?

ManlyMarco commented 1 year ago

Good idea, I'll add logging to TorrentStateChanged on the server. The client is always in the Downloading state.

ManlyMarco commented 1 year ago

Alright, so the torrent keeps seeding, it doesn't error out.

Now that I've increased the open file limit to infinity, there is a change in behavior - the torrent in question still gets stuck, but now it actually does download, but at extremely low speed. The torrent has 1200+ of ~400KB files (1.7GB total, 1729 x 1.0MB pieces) and one of these downloads every minute or so, I think the delay is in between chunks being downloaded. At the same time there's a 950 file torrent (21GB total, 2707 x 8.0 MB pieces) being seeded by that very same instance, and it downloads just fine at connection speed limit.

Maybe the issue is related to having a ton of individual files that are smaller than the piece size? The client downloads only a subset of these files, around 200 or so, so maybe there's a performance issue? CPU and HDD usage of the process looks very low.

Edit: Preliminary testing with piece size set to 153600 looks very promising, no issues so far and speeds seem to be faster than ever for that particular torrent. Let's see if the issue comes back or not...

alanmcgovern commented 1 year ago

Now that I've increased the open file limit to infinity, there is a change in behavior - the torrent in question still gets stuck, but now it actually does download, but at extremely low speed. The torrent has 1200+ of ~400KB files (1.7GB total, 1729 x 1.0MB pieces) and one of these downloads every minute or so

Hrm, maybe I need to do some profiling with a 1,200 file torrent when 1000 of those are marked as 'DoNotDownload'? I can mock this scenario up pretty easily, so I'll do that and profile things to see if there's a measurable difference. The benchmarking I was trying locally was a 2,000 file torrent where each file was ~30kB, with piecesize of 1MB. I also set MaxOpenFiles to 3 to really stress things.

ManlyMarco commented 1 year ago

I'm not sure if setting files as DoNotDownload is important or not, trying to download everything at once with uTorrent after the issue happened resulted in a steady 10KBps download speed. If anything, it makes the issue more pronounced or easier to trigger.

Now that I reduced the piece size when generating this torrent the issue seems to be resolved since I can't make it happen despite doing the exact same steps that caused the bug before.

The file sizes vary from 1KB to 78MB, with most in the 200-400KB range. They are also placed in a bunch of different folders, though I doubt that affects anything.

alanmcgovern commented 1 year ago

monotorrent_as_seeder monotorrent_as_seeder2 qbittorrent_as_seeder qbittorrent_as_leecher

I created a torrent with 20,000 files, each 400kB in size using a loop like:

var buffer = new byte[400 * 1024];
var span = buffer.AsSpan ();
for (int i = 0; i < 20_000; i ++) {
    span.Fill ((byte) i);
    File.WriteAllBytes (buffer, $"{i}.data");
} 

Performance seems fine when: 1) monotorrent seeds, monotorrent downloads. 2) monotorrent seeds, qbittorrent downloads 3) qbittorrent seeds, monotorrent downloads

If you do come up with a reasonably reliable way of triggering a performance issue, do let me know the steps and i'll try to reproduce the problem again. For now it seems ok as far as I can tell. I did try leaving the seeder alone for ages, then connecting. I also tried constantly disconnecting/reconnecting the leecher.

ManlyMarco commented 1 year ago

Yeah, unfortunately I have no idea what exactly triggers the issue, changing the piece size and increasing open file limit seems to have fixed it. Maybe it's specific to running on Linux and/or .NET 6.0 stand-alone? If I do figure out what exactly causes this I'll be sure to let you know.

rjclarkewp007 commented 1 year ago

This sounds like the same issue I have. So I have a little game launcher which I coded (as a total noob!!) whereby players can download a game and then after they have downloaded the game, they also have the option to seed it for other downloaders. So I am seeding my game from 4 servers using qBittorrent. Which works great, anyone that starts to download the game from my application will then download from these 4 peers, but when a player starts to share the game from my application then the application won't seed to new downloaders. Well sometimes it suddenly starts to seed to maybe one downloader, but there are sometimes maybe like 5 players downloading at the same time and still my application won't seed to any of them.

I can even do a test where i start seeding from my PC using my app and then start downloading from another PC on another network and then my seeding side just wont seed anything at all. The download side will only download from qBittorrent on those 4 servers. I still could not figure this out at all.. :(

ManlyMarco commented 1 year ago

@rjclarkewp007 Turn on debug logging (LoggerFactory.Register) and see what happens, if the connections are attempted but don't go through then maybe it's just because all clients have closed ports.

Back to my issue, it seems like it has been completely resolved. I don't see it happening any more, even under heavy client traffic.

rjclarkewp007 commented 1 year ago

Thanks @ManlyMarco, i will give that a try as well. Just as a matter of interest, am I suppose to run something in a timed manner to update or search for more peers while busy downloading? Just asking because I mostly find that while downloading from my app and i start another download from lets say qBittorent, then my application will not download from that new downloader until the download in qBittorent is 100% complete. So it wont download chunks from an incomplete peer in other words.

ManlyMarco commented 1 year ago

@rjclarkewp007 It should be able to download just fine before 100% without you doing anything special. Maybe the tracker update speed is slow and it takes a while for the client to query for new peers.

rjclarkewp007 commented 1 year ago

Thanks @ManlyMarco , Yeah i am still lost with this. Have no idea why my application will not seed to others. :( While bittorrent is seeding perfectly fine haha.

Here one can clearly see bitorrent seeding to 3 downloaders: image

While my application is not seeding to them at all.. :( image

I am still not even sure if it is the download part which is the problem or the seeding part which is the issue here. I tried to do that logging, but have not idea how that works so i can see a log of when/if any peers try to connect etc.

rjclarkewp007 commented 1 year ago

oh and then all of a sudden it sees a leacher and starts to upload to only one hehe.. But i am seeding from 2 PC's with my application and only 1 computer sees this leacher. The other one not.. and both of them still not seeing the other two leachers.

image

ManlyMarco commented 1 year ago

Everything works fine in my app now on the latest master builds, so I'll close this issue.

@rjclarkewp007 If it never connects then maybe ports it tries to use are closed on both sides. If you still have this issue I think you should open a new issue since this one got a bit off topic.