| View previous topic :: View next topic |
| Author |
Message |
HighTime user

Joined: 05 Nov 2002 Posts: 242 Location: France
|
Posted: Tue Nov 04, 2003 4:38 pm Post subject: Dev tutorials (BT conn. control + Emacs/Ocamldoc) |
|
|
Tutorial : Connection Control in Bittorrent plugin
Problem :
Some users noticed that number of connections while using
bittorrent plugin is superior to max_opened_connections.
Knowledge :
Ocaml : basic
Mldonkey : basic
Network programming : basic
Let's start by doing a fresh cvs checkout :
| Code: |
> cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/mldonkey login
Logging in to :pserver:anoncvs@subversions.gnu.org:2401/cvsroot/mldonkey
CVS password: <enter>
> cvs -z3 -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/mldonkey co
mldonkey
cvs server: Updating mldonkey
(...)
|
Now we can start working!
First thing to do is to find how is defined max_opened_connections.
| Code: |
> cd mldonkey
> cd src
> grep max_opened_connections **/*.ml
(I use zsh so **/*.ml means that it will search in .ml files of all subdirs)
daemon/common/commonGlobals.ml: let max = mini !!max_opened_connections MlUnix.max_sockets in
daemon/common/commonOptions.ml:let max_opened_connections = define_option downloads_ini
daemon/common/commonOptions.ml: ["max_opened_connections"] "Maximal number of opened connections"
daemon/common/commonOptions.ml: "Bandwidth", "Maximal Number of Sockets Used", shortname max_opened_connections, "T";
daemon/driver/driverCommands.ml: strings_of_option_html max_opened_connections;
|
The last one is not interesting : we don't mess with html.
So let's have a look to daemon/common/commonOptions.ml
| Code: |
> less daemon/common/commonOptions.ml
< /max_opened_connections > (search for max_opened_connections in
the file )
first result :
let max_opened_connections = define_option downloads_ini
["max_opened_connections"] "Maximal number of opened connections"
int_option (min MlUnix.max_sockets 200)
second result :
"Bandwidth", "Maximal Number of Sockets Used", shortname
max_opened_connections, "T";
<q> (to quit less)
|
With our basic ocaml skills we can't do anything with that. So
let's have a look to daemon/common/commonGlobals.ml :
| Code: |
> less daemon/common/commonGlobals.ml
< /max_opened_connections > (search for max_opened_connections in
the file )
let can_open_connection () =
let ns = nb_sockets () in
let max = mini !!max_opened_connections MlUnix.max_sockets in
(*
if !!debug_net then begin
lprintf "CAN OPEN (conns: %d < %d && upload U/D: %d %d)\n" ns max
(UdpSocket.remaining_bytes udp_write_controler)
(UdpSocket.remaining_bytes udp_read_controler);
end; *)
ns < max
< q >
|
Ok now it's time to use our basic ocaml skills :
we have :
let can_open_connection () =
declaration of a function can_open_connection with one argument ()
here the arg is () which is of type unit (it's a little like no argument)
let ns = nb_sockets () in
local declaration of ns : the identifier ns will have a meaning
only in the code after in
(* a comment *)
We are lucky! The function have a significative name! Now let see
if it's used and how! We know connection control works in edonkey
plugin so we restrict search to this plugin :
| Code: |
> grep networks/donkey/*.ml
networks/donkey/donkeyClient.ml: if can_open_connection () then begin
networks/donkey/donkeyClient.ml: if can_open_connection () then
networks/donkey/donkeyServers.ml: if can_open_connection () then
networks/donkey/donkeyServers.ml: if can_open_connection () then
networks/donkey/donkeyServers.ml: if n > 0 && can_open_connection () then begin
networks/donkey/donkeySources1.ml: if CommonGlobals.can_open_connection () && index < nqueues then begin
networks/donkey/donkeySources1.ml: if CommonGlobals.can_open_connection () && nclients > 0 then
networks/donkey/donkeySources2.ml: if CommonGlobals.can_open_connection () && nclients > 0 then begin
networks/donkey/donkeySources2.ml: if CommonGlobals.can_open_connection () && nclients > 0 then begin
networks/donkey/donkeySources3.ml: if CommonGlobals.can_open_connection () && n > 0 then
networks/donkey/donkeySources3.ml: if CommonGlobals.can_open_connection () && n > 0 then
networks/donkey/donkeySupernode.ml: if can_open_connection () then
|
The first line is interesting :
testing if we can open a connection indicate that we will open a
connection after this line.
| Code: |
> less networks/donkey/donkeyClient.ml
< /can_open_connection >
if can_open_connection () then begin
...
let sock = TcpBufferedSocket.connect "donkey to client" (
Ip.to_inet_addr ip)
port
(client_handler c) (*client_msg_to_string*) in
...
end
|
What we found is hard to understand. Fortunately
naming of function is good in mldonkey so we found a
let sock = TcpBufferedSocket.connect
Here you need your basic network programming knowledge. You
probably know connect (man connect), listen. So here we
created a TcpBufferedSocket sock doing a connect (if we
can_open_connection ()!)
Search for something like this in bt plugin :
| Code: |
> grep TcpBufferedSocket.connect networks/bittorrent/*.ml
(nothing! too bad. Let see what we can find about TcpBufferedSocket)
> grep TcpBufferedSocket networks/bittorrent/*.ml
networks/bittorrent/bTClients.ml:open TcpBufferedSocket
networks/bittorrent/bTClients.ml: let c = new_client file peer_id (TcpBufferedSocket.host sock) in
networks/bittorrent/bTClients.ml: let ccc = new_client file peer_id (TcpBufferedSocket.host sock) in
networks/bittorrent/bTClients.ml: TcpBufferedSocket.set_read_controler sock download_control;
networks/bittorrent/bTClients.ml: TcpBufferedSocket.set_write_controler sock upload_control;
networks/bittorrent/bTClients.ml: TcpBufferedSocket.set_rtimeout sock 30.;
networks/bittorrent/bTClients.ml: let sock = TcpBufferedSocket.create
networks/bittorrent/bTClients.ml: TcpBufferedSocket.set_read_controler sock download_control;
networks/bittorrent/bTClients.ml: TcpBufferedSocket.set_write_controler sock upload_control;
networks/bittorrent/bTClients.ml: TcpBufferedSocket.set_closer sock (fun _ r ->
networks/bittorrent/bTProtocol.ml:open TcpBufferedSocket
networks/bittorrent/bTProtocol.ml: BTHeader of (gconn -> TcpBufferedSocket.t ->
networks/bittorrent/bTProtocol.ml:| Reader of (gconn -> TcpBufferedSocket.t -> unit)
networks/bittorrent/bTProtocol.ml: mutable gconn_refill : (TcpBufferedSocket.t -> unit) list;
networks/bittorrent/bTProtocol.ml: let b = TcpBufferedSocket.buf sock in
networks/bittorrent/bTProtocol.ml: TcpBufferedSocket.buf_used sock 4;
networks/bittorrent/bTProtocol.ml: TcpBufferedSocket.buf_used sock msg_len;
networks/bittorrent/bTProtocol.ml: let b = TcpBufferedSocket.buf sock in
networks/bittorrent/bTProtocol.ml: TcpBufferedSocket.buf_used sock (slen+49);
networks/bittorrent/bTProtocol.ml: if not (TcpBufferedSocket.closed sock) then
networks/bittorrent/bTProtocol.ml: TcpBufferedSocket.set_reader sock (handlers info gconn);
networks/bittorrent/bTProtocol.ml: TcpBufferedSocket.set_refill sock (fun sock ->
networks/bittorrent/bTProtocol.ml: TcpBufferedSocket.set_handler sock TcpBufferedSocket.WRITE_DONE (
networks/bittorrent/bTProtocol.ml:(* TcpBufferedSocket.close sock "write done" *)
|
Once again correct naming saves our day :
let sock = TcpBufferedSocket.create
Do you remember how we declare a variable/function/... ?
let v = ...
Ok it seems that we created a socket with this line
| Code: |
> less networks/bittorrent/bTClients.ml
< /TcpBufferedSocket.create >
let listen () =
try
let s = TcpServerSocket.create "bittorrent client server"
Unix.inet_addr_any
!!client_port
(fun sock event ->
match event with
TcpServerSocket.CONNECTION (s,
Unix.ADDR_INET(from_ip, from_port)) ->
lprintf "CONNECTION RECEIVED FROM %s\n"
(Ip.to_string (Ip.of_inet_addr from_ip))
;
--> let sock = TcpBufferedSocket.create
"bittorrent client connection" s
(fun sock event ->
match event with
BASIC_EVENT (RTIMEOUT|LTIMEOUT) ->
close sock Closed_for_timeout
| _ -> ()
)
in
TcpBufferedSocket.set_read_controler sock download_control;
TcpBufferedSocket.set_write_controler sock upload_control;
let c = ref None in
TcpBufferedSocket.set_closer sock (fun _ r ->
match !c with
Some c -> begin
match c.client_sock with
| Connection s when s == sock ->
disconnect_client c r
| _ -> ()
end
| None -> ()
);
set_rtimeout sock 30.;
incr counter;
set_bt_sock sock !verbose_msg_clients
(BTHeader (client_parse_header !counter c false));
| _ -> ()
) in
listen_sock := Some s;
|
Interesting : we have something which is called listen.
With your basic networking skills you know what it is :
the server socket used by incoming clients to connect to us.
| Code: |
(fun sock event ->
match event with
TcpServerSocket.CONNECTION (s,
Unix.ADDR_INET(from_ip, from_port)) ->
|
We 'bind' a function with the listening socket :
this function will associate event with some work.
You should have a look to the match ... with thing in an ocaml
tutorial but here's how we could do something similar in C
switch (event) {
case CONNECTION :
...
break;
default :
...
}
The server socket create a new socket used to work with incoming peer
on a CONNECTION event.
Ok here it require a little more than basic ocaml skills. So i'll show
you :
| Code: |
if can_open_connection () then
begin
let sock = TcpBufferedSocket.create
...
set_bt_sock sock !verbose_msg_clients
(BTHeader (client_parse_header !counter c false));
end
else
(*don't forget to close the incoming sock if we can't
open a new connection*)
Unix.close s
|
We test if we can_open_connection : if we can, do it
else close the incoming sock (not the listening one).
(I saw it's how it is done in donkeyClients.ml ...)
Let's compile to see if it's ok
| Code: |
> cd ..
> ./configure --(your favorite settings) && make
...
|
It's ok. While coding your patch you will probably have
to know the flow of an ocaml program to compile correctly.
Know you can test your patch... and soon you will discover that it
don't work.
Why? Because we only control incoming connections, we forgot outgoing!
You have to know how bittorrent works :
you download a .torrent file. In this file there's information
of what you will download. Plus there is the address of a tracker.
A tracker in bt is something that 'glue' all clients by sending
a list of known peers. So those peers will be outgoing connections.
| Code: |
> cd src/networks/bittorrent
> grep track *.ml
(some output you'll have to check to find something interesting
bTInteractive.ml: BTClients.connect_tracker file !announce;
for example)
|
In this line we call a function connect_tracker. This function
is in bTClients.ml
| Code: |
> less bTClients.ml
< /connect_tracker >
(...)
let c = new_client file !peer_id (!peer_ip,!port)
(...)
resume_clients file
|
Something called new_client! You can find where it's defined
by doing a
| Code: |
> grep let\ new_client *.ml
|
but you won't find something which create a socket.
So you're back to connect_tracker. The last thing this function do
is "resume_clients". As usual ...
| Code: |
> grep let\ resume_clients *.ml
bTClients.ml:let resume_clients file =
> less bTClients.ml
< /resume_clients >
Hashtbl.iter (fun _ c ->
try
match c.client_sock with
| Connection sock ->
lprintf "RESUME: Client is already conencted\n";
get_from_client sock c
| _ ->
(try get_file_from_source c file with _ -> ())
with e -> ()
(* lprintf "Exception %s in resume_clients\n" (Printexc2.to_string e) *)
) file.file_clients
|
There's a comment saying that in this case the 'Client is already
conencted'.
So we fall in the other case (no connection) :
'get_file_from_source c file'
| Code: |
> grep let\ get_file_from_source *.ml
bTClients.ml:let get_file_from_source c file =
> less bTClients.ml
< /get_file_from_source >
if connection_can_try c.client_connection_control then begin
connect_client c
end else begin
print_control c.client_connection_control
end
|
Ok here you can find what connect_client do or simply prevent
this function to be called :
| Code: |
let get_file_from_source c file =
if (connection_can_try c.client_connection_control) && [b] can_open_connection () [/b] then begin
connect_client c
end else begin
print_control c.client_connection_control
end
|
compile and test your patch and... it doesn't work.
I'm a bit unlucky. I thought it was an easy fix, but it turned
out to be a little tricky. I won't explain in this tutorial what
to do to make this patch correct, but if you are interested
you can have a look at the [url=http://savannah.nongnu.org/patch/index.php?func=detailpatch&patch_id=2247&group_id=1409]
final patch. [/url]
Make your patch :
| Code: |
> make clean
> cvs diff -u -w > name.patch
...
|
Conclusion :
At least for the first part it was not a difficult work :
with little skills and a lot of good sense you can contribute
to your favorite p2p client, fixing little glitches here and there.
Tip :
use a good syntax colorizer :
Tuareg for emacs
Last edited by HighTime on Wed Nov 05, 2003 2:21 pm; edited 4 times in total |
|
| Back to top |
|
 |
HighTime user

Joined: 05 Nov 2002 Posts: 242 Location: France
|
Posted: Tue Nov 04, 2003 4:45 pm Post subject: |
|
|
| Stay tuned for another (hopefully easier) tutorial. |
|
| Back to top |
|
 |
pango Sage

Joined: 31 Oct 2002 Posts: 2436
|
Posted: Wed Nov 05, 2003 1:26 am Post subject: |
|
|
Good idea, I like it!
While grep give interesting results, Emacs tags, or ocamlbrowser can be useful too (I use Emacs tags more often). If someone's interested, I'll write something about their use, it's not that difficult. |
|
| Back to top |
|
 |
HighTime user

Joined: 05 Nov 2002 Posts: 242 Location: France
|
Posted: Wed Nov 05, 2003 1:30 pm Post subject: |
|
|
Every tutorial is welcome,
especially when it's about being more productive with emacs and ocaml. |
|
| Back to top |
|
 |
dvj neophyte
Joined: 08 May 2003 Posts: 7 Location: Norway
|
Posted: Wed Nov 05, 2003 1:37 pm Post subject: |
|
|
how about putting that into the wiki? great tortural by the way  |
|
| Back to top |
|
 |
pango Sage

Joined: 31 Oct 2002 Posts: 2436
|
Posted: Wed Nov 05, 2003 2:01 pm Post subject: |
|
|
Ok, when you have to manage many sources files, you can generate an index of all declarations, called "TAGS", which can then be used for fast navigation.
The default tool to do that is called "etags", and it knows how to parse many languages, C, C++, Java, Perl, Cobol, Fortran, Lisp, etc (see etags --help for complete list). Just do
or, if you don't use zsh,
| Code: |
$ etags `find . -name "*.[ch]"`
|
... and you have a TAGS index of all C sources in your tree
Problem is, current version of etags doesn't support ocaml
So you have to use "otags" (taken from http://perso.rd.francetelecom.fr/alvarado/) instead:
If you want to index both ocaml and C sources, you can use
| Code: |
$ otags -r .
$ etags -a **/*.[ch]
|
(-a like "add")
And now, what ?
In Emacs, you can use that index to find some declaration; Type M-. (ESC . usually), then some variable or function identifier. You can even use <tab> completion! Press enter and Emacs will open the file that contains its declaration, with the cursor at the right place (the first time, it will ask for the TAGS file path).
How to search all occurences of something ? Type M-x tags-search, enter a string, and Emacs will display the first occurence of that string. Type M-, and it will show next one, etc.
You can also substitute string with M-x tags-query-replace...
Last edited by pango on Wed Nov 05, 2003 2:19 pm; edited 1 time in total |
|
| Back to top |
|
 |
pango Sage

Joined: 31 Oct 2002 Posts: 2436
|
Posted: Wed Nov 05, 2003 2:15 pm Post subject: |
|
|
ocamlbrowser is another graphical (tk ?) tool for doing identifiers lookups. To use it, you must first compile your sources, because it uses ocaml object files to do its work. Then run
| Code: |
$ ocamlbrowser -I path -I path...
|
were paths are directories where modules can be found. If you want to add all directories,
| Code: |
$ ocamlbrowser `for i in **/*.cm?; do dirname $i; done|sort -u|sed -e 's/^/ -I /'`
|
should do the trick
Once started, you can list modules declarations, search specific identifiers, etc. What's great, is that it shows the ocaml types of identifiers (which is sometimes god sent )... |
|
| Back to top |
|
 |
HighTime user

Joined: 05 Nov 2002 Posts: 242 Location: France
|
Posted: Wed Nov 05, 2003 2:15 pm Post subject: |
|
|
| dvj wrote: | how about putting that into the wiki? great tortural by the way  |
I hope that there will be a lot of questions/interaction so i think wiki is not perfect for this.
Thanks Pango for the tutorial! |
|
| Back to top |
|
 |
HighTime user

Joined: 05 Nov 2002 Posts: 242 Location: France
|
Posted: Wed Nov 05, 2003 8:05 pm Post subject: |
|
|
Tutorial : Drop BT clients after two unsuccesfull connection
Problem :
Some users reported that memory usage grow while using BT plugin.
The idea is to really drop clients after we failed to connect
to them 2 times.
Knowledge :
Ocaml : basic
Mldonkey : basic
Networking : low
First here's the sources layout of mldonkey (as explained in
mldonkey/docs/developers/sources_tree.txt):
| Code: |
In src/:
daemon/ Sources of MLdonkey daemon
common/: common types used by all network plugins
driver/: drivers (main, interfaces)
networks/ Sources of File-Sharing Plugins
utils/ Sources of useful modules
cdk/: general modules
lib/: general modules specialized for MLdonkey
net/: scheduler for events (sockets)
|
In this tutorial we will put our dirty little fingers in
networks/bittorrent/ and utils/net/basicSocket.ml
Let's have a look inside networks/bittorrent/
(BT plugin is great to begin with because it's very clean
compared to edonkey plugin)
| Code: |
> ls
bencode.ml bTComplexOptions.ml bTInteractive.ml bTOptions.ml bTTypes.ml
bTClients.ml bTGlobals.ml bTMain.ml bTProtocol.ml
|
bencode.ml : describe is a module used to decode/encode bt
dictionnaries (used between client <-> tracker)
bTMain.ml : main file of plugin (intialize plugin)
bTInteractive.ml : input/output functions of bt plugin (load_torrent,
cancel ...)
bTOptions.ml : define bt plugin options (client_port,prefix,...)
bTComplexOptions.ml : save/load structures use in plugin to/from
bittorrent.ini
bTTypes.ml : definion of types used in this plugin
bTGlobals.ml : glue plugin-internal functions to common/ functions
bTProtocol.ml : parse/write bt protocol ()
bTClients.ml : client<->client work
We will take a little shortcut for this tutorial : i'll show
what will be done when we can't connect. When creating a
tcpBufferedSocket we do something like this :
| Code: |
| BASIC_EVENT (CLOSED r) ->
begin
match c.client_sock with
| Connection s when s == sock ->
disconnect_client c r
| _ -> ()
end;
|
Do you remember how a function matching CONNECTION
event was 'bound' to the listening socket in the
"Connection Control in Bittorrent plugin" tutorial?
Here we have the same thing except that the function is matching
'CLOSED' events.
So when this socket will receive a CLOSED event we will
call the disconnect_client function (with the client and r as
arguments).
But first, do you see this 'BASIC_EVENT (CLOSED r)' ?
It's a 'parameterized' type. The definition of this kind of type is
'BASIC_EVENT of something'. You can search the definition inside
mldonkey source and you will find it in
utils/net/tcpBufferedSocket.ml
| Code: |
type event =
WRITE_DONE
| CAN_REFILL
| CONNECTED
| BUFFER_OVERFLOW
| READ_DONE of int
| BASIC_EVENT of BasicSocket.event
|
Now let's see what is the definition of a BasicSocket.event
(it's in the module BasicSocket which is the file basicSocket.ml
in utils/net/)
| Code: |
type event =
| CLOSED of close_reason
(...)
|
Ok, so the type of the argument r passed to disconnect_function is
close_reason (in basicSocket.ml)
| Code: |
type close_reason =
Closed_for_timeout (* timeout exceeded *)
| Closed_for_lifetime (* lifetime exceeded *)
| Closed_by_peer (* end of file *)
| Closed_for_error of string
| Closed_by_user
| Closed_for_overflow
| Closed_connect_failed
| Closed_for_exception of exn
|
Ok now we have found how to detect a failed connection :
we will have to do something special when the reason of closing
is 'Closed_connect_failed'.
Let's get back to the function called on a 'CLOSED' event :
disconnect_client c r (it's in networks/bittorrent/bTClients.ml)
| Code: |
let disconnect_client c reason =
if !verbose_msg_clients then
lprintf "CLIENT %d: disconnected\n" (client_num c);
match c.client_sock with
NoConnection | ConnectionWaiting | ConnectionAborted -> ()
| Connection sock ->
close sock reason;
try
List.iter (fun r -> Int64Swarmer.free_range r) c.client_ranges;
c.client_ranges <- [];
c.client_block <- None;
if not c.client_good then
connection_failed c.client_connection_control;
c.client_good <- false;
set_client_disconnected c reason;
(try close sock reason with _ -> ());
c.client_sock <- NoConnection;
let file = c.client_file in
c.client_chunks <- [];
c.client_allowed_to_write <- zero;
c.client_new_chunks <- [];
Int64Swarmer.unregister_uploader_bitmap
file.file_partition c.client_bitmap;
for i = 0 to String.length c.client_bitmap - 1 do
c.client_bitmap.[0] <- '0';
done
with _ -> ()
|
Now it's time to work :
we will have to check what is r :
| Code: |
match reason with ...
|
if it's a Closed_connect_failed let's do something :
| Code: |
match reason with
| Closed_connect_failed -> (* do something *)
|
if it's not (aka something else) do nothing :
| Code: |
match reason with
| Closed_connect_failed -> (* do something *)
| _ -> ()
|
You already know that you don't need to worry about memory when coding
ocaml : you never do malloc and free. So how to 'free' a client?
Garbage Collector free an object when an object is not
referenced by any other object. So we will have to
remove any reference to a client and wait the GC to do its job.
Where are clients referenced? A client is a file-centric structure :
so clients are referenced inside the file structure (in bTTypes.ml):
| Code: |
and file = {
file_file : file CommonFile.file_impl;
file_piece_size : int64;
file_tracker : string;
file_id : Sha1.t;
file_name : string;
file_swarmer : Int64Swarmer.t;
file_partition : CommonSwarming.Int64Swarmer.partition;
mutable file_clients : (Sha1.t, client) Hashtbl.t ;
mutable file_chunks : Sha1.t array;
mutable file_tracker_connected : bool;
mutable file_tracker_interval : int;
mutable file_tracker_last_conn : int;
mutable file_files : (string * int64 * int64) list;
mutable file_blocks_downloaded : Int64Swarmer.block list;
}
|
We know where are referenced clients : inside file_clients.
| Code: |
mutable file_clients : (Sha1.t, client) Hashtbl.t ;
|
In this piece of code we see a declaration of a member of the file
structure. The 'mutable' keyword indicate that the member can change
during the life of the file (file_id cannot change).
'file_clients' is the name of the member and
'(Sha1.t, client) Hashtbl.t' is the type of the member.
What's this type? It's an HashTable which associates keywords of type
'Sha1.t' with values of type 'client'.
You can check ocaml doc to see how to use a Hashtbl.
In the definition of new_client in bTGlobals.ml we can see :
| Code: |
Hashtbl.add file.file_clients peer_id c;
file_add_source (as_file file.file_file) (as_client c);
|
Here we add the newly created client to the file_clients member of the
file shared by this client.
The second line is interesting also : i could have missed another
reference to this client.
Now to remove references to a client we have to do the opposite of this
two lines. Let's create a remove_client function (in bTGlobals.ml):
| Code: |
let remove_client c =
Hashtbl.remove c.client_file.file_clients c.client_uid ;
file_remove_source (as_file c.client_file.file_file) (as_client c)
|
You can learn more of what's in the 'client' structure but here is how
it works :
| Code: |
|___Client__ | |____file____|
|client_file |-------> | |
| |<---1---|file_clients |
| | |file_file |
|___________| --------------
î |
| v
| |_file_impl___|
\---------2-----|____________|
|
With first line of remove_client we remove the first reference
and with the second line, the second reference (file_remove_source
is the opposite of file_add_source and is defined at the same place).
We'll have to use this new function on third failed connection.
So we must introduce a little counter associated with each
client (in type client in bTTypes.ml).
| Code: |
(...)
mutable client_good : bool;
mutable client_num_try : int;
}
|
It's mutable because we will increase it for each failed connection.
We need to initialize this new counter in the new_client function
of bTGlobals.ml
| Code: |
(...)
client_new_chunks = [];
client_good = false;
client_num_try = 0;
} and impl = {
(...)
|
Time to go back to our disconnect_client function :
| Code: |
match reason with
| Closed_connect_failed -> (* do something *)
| _ -> ()
|
Now we can do something :
| Code: |
match reason with
| Closed_connect_failed -> if c.client_num_try = 2 then
remove_client c
else
c.client_num_try <- c.client_num_try+1;
| _ -> ()
|
We put this piece of code at the end of disconnect_client :
| Code: |
let disconnect_client c reason =
if !verbose_msg_clients then
lprintf "CLIENT %d: disconnected\n" (client_num c);
begin
match c.client_sock with
NoConnection | ConnectionWaiting | ConnectionAborted -> ()
| Connection sock ->
close sock reason;
try
List.iter (fun r -> Int64Swarmer.free_range r) c.client_ranges;
c.client_ranges <- [];
c.client_block <- None;
if not c.client_good then
connection_failed c.client_connection_control;
c.client_good <- false;
set_client_disconnected c reason;
(try close sock reason with _ -> ());
c.client_sock <- NoConnection;
let file = c.client_file in
c.client_chunks <- [];
c.client_allowed_to_write <- zero;
c.client_new_chunks <- [];
Int64Swarmer.unregister_uploader_bitmap
file.file_partition c.client_bitmap;
for i = 0 to String.length c.client_bitmap - 1 do
c.client_bitmap.[0] <- '0';
done
with _ -> ()
end;
match reason with
| Closed_connect_failed ->
if c.client_num_try = 2 then
remove_client c
else
c.client_num_try <- c.client_num_try+1
| _ -> ()
|
Conclusion :
I'm not an theory of languages expert so i cannot guarantee that
those clients will be freed (but i think it's ok).
There's still too much client doing nothing : we will have to do
more checking in future.
While reading bTClients.ml i found a little bug :
we sometime 'leak' a client. I let the reader find this bug
as an exercise.
Hint : it's near the creation of a new client.
The solution is in the patch. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © phpBB Group
|
|
|
|