From 948542394e26df4f7b72504c836270b0b0dbeed0 Mon Sep 17 00:00:00 2001 From: taptap Date: Wed, 24 Jun 2026 21:49:50 +0200 Subject: [PATCH] feat: Implement reliable UDP transfer with retransmission - Add seq_num and flags to udp_packet_header_t for reliability - Add MAGIC_UDP_ACK packet type and udp_ack_t struct for ACKs - Add UDP constants: WINDOW_SIZE=16, MAX_RETRIES=5, RETRANSMIT_MS=100 - Server: Send cumulative + selective ACKs during UDP transfer - Client: Implement sliding window with retransmission - Client: Add send_file_udp() and send_file_udp_single() functions - Fix server make_path call to not create filename as directory - UDP mode uses single-file protocol (not batch) for reliability Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- fastsync_client | Bin 31728 -> 36024 bytes fastsync_server | Bin 31544 -> 31544 bytes src/client.c | 459 +++++++++++++++++++++++++++++++++++++++++++++--- src/client.o | Bin 19752 -> 29224 bytes src/common.h | 29 +++ src/server.c | 128 ++++++++++++-- src/server.o | Bin 21880 -> 22968 bytes src/utils.c | 1 + src/utils.o | Bin 4944 -> 4944 bytes 9 files changed, 576 insertions(+), 41 deletions(-) diff --git a/fastsync_client b/fastsync_client index 3056e44fd896563effe09ff5ee9a3b9f956a3165..da2f6eedc1c7689ed2c0fc6118b9bdae16414a32 100755 GIT binary patch literal 36024 zcmeHweRx#Wwf6}L5UDs(!Q%I*!3LF(@MVHTGb96NbPy_Fv5G@TCXkX$;>-kt^REoDo#27LclWf4}`PnTeTu@AJO* zdH;DidCsi8)?RzJ3JMH;6&jZt#N1;frB+28U9JKEwMLaO6u;*h z=NN;47YUk-uay9?^4c>X7ZxjeAt2L@V4xp3OOg!@wT6UDm+yLTxQx-zD3LhR4P?;G zA6#znn}JVPN?gP2bRxg`ioXwB^EFA=FgqP>v!+eO-bn8?) z4NH_gHPrT`7~Zvu@zWoaW+j&}L)Udo)tH7dV+^)O%AQ$zX*uQ8#Pt}&5ldpI~|!lf;@jGH)N!nn#yCoIHI zRmYO9@3efU>Xr%N#z@on(CCG&Eeje)k1ZcRcJ#!`F+j$&HjQp=33Yaj?y8zNdgA!< zNPGEMDR(5m6}T&KpMv{*+=FqK;x5NM33oMaH|`qTm*M7hi3A4SbRzCixWA5j0`76R zFTh=e`)u50xQF8Abq?-Jai4{IJnqT3FT&01LJ17&bh!%uS3sFdu;r@{HtCa;ppz98 zZ4coSai6Ma1Z+O6D;E)5BP0;Nr{S0Nf3Bhp0X!YIt|LVj;7t5pjQa%KX9$X6uq!wZ zH%(7}S6!Mh8sTBMd3{3ygF4eTlr73Na2Y~H14v^%&^|8(|JYXjv3=HW9y!9Q>-Sm- zYJZW-AhT%Cu0TAqCWoJanj^6(GLBmb5>{8RJDpPNU{_B{NL z=b=x^lkd;-(0`Lh&crtoV>v`nQ z%tL=RkDTYhe}3Ogf&T#XlQ}}%DI@hf$fxN0mgUlofN%EnKvOUrT-Xwc2E((b*R{5X zg0mYIv<8hpU}0N(C=iJ@grk9gWbK!+Bf2OYY-oaVNI-VrrAg5hv`*oZ{Kp~jA-Mq?}JfrY_nw52U*G>3!Yr=iIRcC|zq8wobG2=;K} zqOj4?-r8z3w|4|XMr0|pZ8Io07&Y1!Hv#LQ+*U+tbsF14O@WpWL}|)*+FL?KxQ#+J zsjV{_?1JV<+rE^N5ElrvK=RmeD0(>B-kRYR0wXO8Lk(FplspfRxCO<$DbgN} z8qJYlaB(~Gg`%PM0QeiCE$tzrB@~PXI-&?s`{ow-P>$X=H=r(Bnb<(7RTO8B6~r4E znrKY(`=-nbo2uG6 z+B%jtw^T1)7zr(0+Hh%Q!{Q|qx>_1T72`%PY-}7|IiY-Pc?D+2m?XC>U|So2xz_;v zajr+mzk#@WF-^^egH$|cYlFdEoZ*+!DD|rn43(Fb#QEM}!$RhIy|Cfl2Qd9BHh!RJ z_X~Wm@vy>wBk)1S&lUbdfe$pERCxIG7ED2ljAs@8q=+vxey{M(GZ;U>*bSW5n%gAQ z{g7A~fb>`&dP+@RQ+@g;ji>IAayWNYq6J|`7G2N58L=yi?iQT#XHOQL>jr)8%c65m ztS{YPQg+T7;!epyI_J-sD<(r+^PtPkT*WaNBwcL@2tr8~U9SZhF(Ql3GV9A7lOf9C zt*^3}43U#$QR-Ya-%{V*HhK?W@E)V*HhKz1AY$72~g@XOFY7m>>(=WUs?kWYLF7 z7V*0(i_Z0wz8;Lp5X;4Nn!eV=WQcUGPxbX!Oom9$UgNFHqH}GhuMII7BEMeGk&=q> zSC)%wK7H+o$q@O^(nP>rF^Q1QIjg?*#AJx{b2JffUrZvTpDTmn*NI7l^z&p;{2q!) zg!J=eQ2ZW^NrZH+%`=y3zoeIDi15iQ`p7JLNf!OWEc%En`Zu!Z?ksv)7QHNsJ}Qe| zkww@00*tQ8qH|rXui7m7C7K9mX3@F6*4NA|I`=p9H8+c{*X4|xpGD^$iM|$P(YXhr zuZ}GGSWN`%%A#|BL|?Hi`glzQT#-eekVRjWMd!YYz8=h?bKgZ@YqIFv$I;hgS@cPo z2)HhbuJ?MfqK^lUTj00_j$7ck1&&+bxCM?|;J5{jTi}1x0-w0fIcCNW7Mu3KEdfm9 z6RGHco?T{qTk%$rPtSzkf!s6l5BPPRTZ<6!MT|@D>%lehIpPJxcL{tWac)7RQv!d2 zIG6P4bprnd@gm}D1pWwdZWE+e3H(RIxuj3W1b!cJE|=0B0{ zxdcz|6ZjP3TzaQ>349W9F1gbwfsZB5rFMFqz%L>`jQARXpHG}i>+~vtpFx~U>U2!t zrx52-I^7}gA;h_aPR|$k0ODLar)LWM7><4xamk#n75K-*xl~S92>gBGTq3940^d)Z zOXGBjz+WSN4sk=^FB3nP_@OUY{@)RI6W=HB=ZK$2e3!sC66aDlof7yH#JL1cuM_w$ zh;!+iUL){Fh;zxCUM27!5$94j9TWI{#JR*xcL@A@#JRLh&lmWe#JQwR&lLDA#JQAB z*9tsLoJ-hrg}}cbE%p(1bz*1E>Y8mJ{R>*oJ-U6K7mgm z&LwGjm%t|x=TbDC68Ko+T!N<83H&1BTzaP02>g8FTymyY3H%J=TxzCc0-tn)VKlD- z-tT25Yo<&_yR{!Op`71dUj^Vy6yddO0KS6PJ@)0k`1T@a4azgU`rXctOzSOY9E6+p zCbk*p8wjU=2p@;EkFW~$TaR}!sr9;Z5I$hqNuvJbCHC~{gHe~qo?PmSZ!2yV|73c7;a*mXo_mFc5 zIMXRKI={WT7WvlOo4No{x{INaxp;4t-&(y06vq!vzrB`Wv+yAkz+e40*YYEVVfgG@ zOGluds+?)ZP*3k6^#Te%ox&R-9E4U9ib=Re68t0_p$R;a@CFI*g5a(G(sk$BGYq5N zT0bB8q&?`gAmSM?#J3hW&w=N%hZy5pelN3+KVOx$(D8xiZt$owcq`v_eh60I;%!x? zy`CCF#{$#7bc2(y!!sGp%y`7v(y+4*Z4Wmw$u&Xl&i4jd3r=;_5cUQ&{0W`(R%z8q$lO^4g5P@289~mUq6-D*zmxN3L~Ia__H{{J zO=<&GK+|)&(A%ZYM3)KvzcXfiXhY}g z%6LoxMj*yQ%=MldJo7v^dIIy(Z$Q=Na@V=bA(9gUV~%OP53Po&Q)!dtfnH>H! z_SgK@yY<#bP6*k@kHW#$LQLFQ6hAuDb@%z0ESQZ&akF4g&py9hbHa6oaiH6EZj5=E zcFkvV4PzrG1eJ%K8aRcOV5SGwy3VZ^@qfvRKO6BLYx{u}x`>z;J&kzN66&SfP%9#x zECVf@z7Diz%`ZzS=pN8lnek1cT(0Hc9cUQS6VN@3zy?kr%;fsokG_Np^mwYbdt6s< zPo%6Z8<^HLV~dqo4lSJ<;7y5?YsJOro$O7tAVehQVm=k$B&_IK{u<0~CI^}rGhnT| zacW~)+h?NFX2z8%RN`CcG_HYvQ=;EG=1)Gp>&TaIcwq=<6ujO{ZrTM^DpODMRK~P6 znPC0I{C7Js7vzz~A;Y-t#_q&yP@x`9bYf6)`RP!@Og@wX6+Nvx z4#Es-uxStSgK||uxrdm$mD~dk{Qi?VedcUE%*K$eJPfuUx+!~$^w5{(@5Ti5b` zFpylo1`(oduuFRM9}sPCVp@z>wE*?@|{L@ab(Dl{@ru7%p zWWBX`MCD-*d$jo0GSjNthoOTN;fr?{qiD}y=2~w>jv~gfMLX!O`OOVGK;!yq zQNGTfA)Qd&eux=EN6%(<34U8>=P!I*=$h;*&22CCWpMMmAjo5XJ@K;F<$V{56eIUC zfAxXReJJ-jh?9N7uRo=26C^adK1!JvC)ObNvFW-f1$-x>HUJ0tF_K*+!yiDm#poR$ zyKWqMFA>*u&-v}qf1JbM+%2X(;kO7*Pki87u@{A+l#hL`i(Y7UmF@9c@52{=fDUB? zmj@8>+Q+Vwg>fKe=#@g;jlTyi*@gU^d!ZdN6$sL*L?t4Dy`D}0aVyz#EfnS0B)rcq zh409UbY^_Q@@=9SojQbV;lOZ##JAz)l__uHZP#kJXK+t%7q?Zns{|J{sIiMMS3~o9 z2U9vTIpJJj>3@R*`7H-Z2{XOq#P=7NC+t#|nY%MR6cxoO88Y6;<3=PLLNS=O?EM%~ zfEU2wgg4%XK5g?D7zEzqydX4AGT~+>gnNU@pR6r_CKhIq(_Qu5l__U6_$m)01;VF8 zlE>P{Ug94Rv++^rz1ag3BQc_n!8qvB5~-yZrq4hWx~h7{9fK?E>Y?i%aR#|DURQU! z5|2^QFo@>$)`CLP6Zi2^J-{b znGz*(UZVkIjPo$U=_tB3@cdC2c;IcAO2b_L3t0M6F**$l3DWSNkipuXJ`Yst@e&eK zkDWqb&F>NI)JRXDPR@CtOFy3`s5K?CI4K%d3h|pJP$B2T3`;3kDD+lP4*W-N`{w$% z@&ljs(v7CI-;BR~Xy$BhWvX%~T(1CQMbGf>&NPfqd-*}X=^B<`;CBeBk4A@M2)PkV zuwV3qo_%Uu+N#H=WQ|AgPnbk7>Pyo)WN!KJ3UkZRLbG7I`O=rsVGuA{2`KK_C+knA zr+%@T8l*KkFP>w@YleUmW_l-jB8I*je2sLpzHR5!@*ZD4{43;^y!-s*;|C( z5co`cuxYR2;}NDk$h225B&H^2_3y&P=-aDba^08Oni>D(e86WN!F*tjk23*(%h@zb6Fxf?GK>5XeMtX(CuASH`_A> zs~pDHTfzIgy$=ex-QFJ)`mMEHh(QnD0pP9tVAWOjG8IN514U zrEFl%*AB5I6?h-SOw4CbtuPC`KZa*)1OF7O=--*(^vcX}<1oZ~?Ar|INzUfwy2*?m9p*}W4-FC>Di!BIDM1y0KRLg`mn?b_BMf!t zh((a}u0wFXpd>2<5KBC6!eq~nFo+ek;l~?jsHXw;3Cu2MN_g4zNy+w{Cn?6X;nV9xUxxV;WH~RN z$z;#j-Y3oK=Uj<^NV1*<6M1xdS5TwMz4i7)RN(E`R=#XnTMnGA{60F>XP;PV+H+Qz z1zX`nw@(zgjC!fw!VF-}3g;8(k913SJOA!u0#pyB$KN8r8{1Lf3gGE@nKKT zA+z9Ya|Sd@)}*m`N9QYyX%}+zQ<_(%e9N}62AzpWVa5|w7G5V=FMEh~cO`zo?#PxV zNACujX^Hi(V?YrMCo@5EK0PS7m$x(=_BkD1^0zx)2AFF#a z_X9mbBGw6@lkDs}px4v6X_&E4(AoS|j+K}OG>&E&nQAOR+#G$Zr+kAZnhFnAn5!Y#+2It8`1Jm()HwU zo`CUbgFn*o<#vPOlJ;1?3S99ncfskYkq1v4JZs>$FmPB2Qk|(IASZqlZKPSGen{## za6I~Y@22Yy$53fg%B48dM$ccKa?OQSk?L&4M4gGyU>NJS_FZW` z^G*dSI-V-9guM9CD{lWa)(<3NMnEU9o-?gCo&SZARV?#nV@v1iq7cvZ~YzB(iNXK!0DH)1giF>jcn5C3p zXC(B%0qoUMv-Q+o?G?3<10AhYntkmz$SLdvWl|JHM{;%z7uuG%61$n#YR1Hm4R9@= zh$>9H>{@;W%o#rhFBy)2o!EwWRXvr5twfVR@1{*)i4nly7|+0f9;=&MjJFC8#aPG)aW08@+}ZKA2N2$e5m3&OeFkp);$CTf5Ye(Phu^`+Cqn9C9rvXJMTffy}klrv2~zK zhi+Vm!n6l5)!V@?G?==*axW$ipdA<@+YOcQ*w1dl%|=P~x2)FkY*Dc+EtYk@f|otj-Coz! zBlXpXUGdcz`~23+=EbX-mGz0o`qbQT)pd7O?S8&(JX+68%09Ax!wT+!Zh3!*Klz;k z@8S=dJk`(FyQVqZS@tBSp>{9}C9B+0o^HKbk0SvPR@K|HD!k}R68lj~TeNP!b=17L zu8gz5_#Z&U5L4=je^KPR``^${#Qx)M5-wVJMI z+K~|$s*?3afd?~i%**J#aIoLu-YT68tZe6)rO`*V8)jVZA{Z53FIc3pD|bpC%yB+5 z{#=<8dYeA>-Lj|YCD_VAImAXzmACp+SHgfdO}CDTy+mvc)0T;yuI1-(=&(1jHpQF* zO_!!}zKM*{*?k{-pGNc~=jHwA6gZ8Ip_8#U(YLBM(SKYkk6;7dYkh{!l~l+w0ld}= z?EaDqN0^IKRc7*r!80-6Y~cSIioJk6fVl#<8$y`p=kK%l=3YU)T%8@Wq~)1OG`7i++*j<2bS6 zMmFpC(Nb694WzVJlX=-O54tT^;ta;wVqR%hC;r525^KpN2C~Z%uM5f##B5%SJa%#) zNcMW#96Kr%l_|gVT)jQSo7mZTLiYexd3<{TW+!lks_HG_f%Vl#!*6-4zjLDnOMbC$ zk{ni9Uwtf6CMKq@?7v{vu;^KKRidZ!v~KY{yC=S-AbUOz)QqR9;-9%Yx5c-+F?T8Q zElct7>t$cYkYnegs5NR-e_4{1?@E3GXHcg;$f(NNC91zr6yd;GLW!*LFaDg6@2P$@ zI@mTVP?QfWbz~$?X?SWA{~>lvJjp;0Ryr)Z^-B5;WGzH_(Lu-|LAOB6#Mn%Go96Vz zZ&&#X*U~3_STz=V?bgx~H6m~zC@tl0J!zTYqqWpc^S)nSy`EY6>}cr-wAerB)`=Dl z^m*M#3sSNvJGa4Ql&5iom{ZnIOLcbaQ|>VjEzE;D^I11wDCw>*6+;4w966=lI&`t< zjN?aLuDed-IG~QLzQ;}?bA-Y3fm*ip{oHgqkE=K<$-csQ8M(woD=M%BrMs?zKGI!R zDxOaGD~yVH?n(CQDzLLlY!DtPh8F4GSSzLE`ny|L2B|UKWIyh`dPY6=KG|V87G`I1 z@(ghkRRjCvP1a{vf>)B{1!wpIw1HzcQ#M7j?;+J#HFAo{U|B3eo&G2!5fH)KYc~*hz;0 zy2af28Z=GQo?GHPi7B1zAZ~(Ha@ijf?EFQhIFHP*P6cax6C=<~z6WE=beIWniLeoN zGMocgUO@1Pt^{8l0TYeqY~tn3T=dE17=~GpoF`C!R)TX4_9TBpIWnun6mk%2K&&iT zrJiS)8k1b1Qmx_K^#-FrVwj6nZc@%G$b&% z`0Gxbr71ttb*`44owQ&A}(-+ZaZ$Y8sn;3x}`$^ha7#NN=0q3M}v(q3eJs8_wP{#lpV(Mhf z=dW{sh!1=PXY>W>Q-nh7zLwsm zfm6R1^zUGr-xY>|w+SH#JsxSf<{obY222VrsW+NV0LU_y`5gDbra z<-+h>OoQ7s3#72$hy>uAlErxz*kQCQz_JmG@J!2H2*g>MaRldAD7ETE^rH)#MI){$ zlG5LSl~LdCf=@g?g$b4_%inNjh;l~H5)99iVT{OC7xp>H@GWEs36GNDB$nb|>DQV5 zRqqGC=(*Ns9nNeOVZb-7O;t#QfusVU-b$1TkaPpYE;n3>Wo+NchjzV1rq9IT0p}{i zKS7S%pt|=pM!e;W0`yti#KLkThlXULRtQd%0rc4~<$3#D#&Q|@Yq-9dG*69nom$>G zD7Lh~h`uGlrz2e6d1`Fw03-TB?)e%NLUNDL@cSbD@C8sv9Cbn)nBud)K)jt~;e^sW z?7;E|yN}i$sK51e)X|>>&w47TqPHhr#MX@gfb!yb6%>m7*0aEH5C}eT2wP*+vYlOy zjO7_@;$qkwM}3#ArukgSkuch`Be1BlR*wL|SxFQCNo@;Ev~ z`zDiA5!yWf?UbpV3MffV>n~bDa`+O&SMFtET_MZ5#eQ?4VE($|M5F0ImYA+HW6WXf zVY~g-o!ndClzBURmJ==ON8EfyotWKsmT)DA%VQ08o}zhB{KSo4!kMz?Q>;H{53R{z z5N?z;2(qeu)((^+fc-GP^>@^VI)ZkZkS5&>X=?u&9T8Q-$us7iI0>iq(2#|lY~h%W z=L^}RBz7Rcd^;dx1I*oXAfD;p&_o7t^G(*VANwW{R85H|3*|grH9#L0-qvTL`4!?>MCsRPdqZD^S#SQFW?jz8L&JP=BL`End%JO7b(gaCA(2A)RPh?B`o_u0;-ISD$lw(o>xDpTpz zsQfH>vOklX-ahMd6Q}yz`+g8J17{8OWWRDHx@sI|@%)J$lmkhE{%^<&D-YS<4EDsj z(BZr(I-C(i$~#X6K$r8o-u^SwgK#)Y`I7!pK1FZ`cKV!qnH?S>;cx->18_bBDUs^> z9(oL***!*)?lGVcCf}~7Ql5h6ovW2*iDp5_RIW zSC$qR(cve2hQltex8y zdarZr74~pUgj1qRefIdbxW+Z@dqiWg?-IvT$p=Iya}Ihp#J(kl^qY{d`XMH~V<*qf z8-O#ZGne`;+r~P{nIyA+(3bj?%&J^-(JfFwt*pQ3WHlai4Kt$esqvs|7{`OI;YRe& zIqeJSxG~`r$+Vr@C}`TB&?@*mVe_iWy@|uOpW9oX8SQ`baz^_DO!HIVLi-<~jOiL( zAHv@-bnec6uF87FOuQVOCdy;Irk;)3j}oDpx`swCHb-rZj`9_3JMh<4&h?OsZHMUY z>&@;Ir1r>%x#sc6wjS0T_2%{1Rnx>(|4DxJx%u4*!N~9Sq0Fs}x&006{OAxT{gTY_ zPknP_`s~bzBR|=#oXs6694KAuu?`ET_sl_E?L+O#(=4>M0mb*@1;_RPSyz%iz? zc>8h@GF(q3{t61ZErR8#&NB}9v&RqDQ*~G{Z!K7!ijJQ=rt{GDLM(M~;wW-*ZiMQ1 zlmgxwow~hnOfPfiD|+0>F!QOQauyZLqu_2-u-@9I#v^TCXYVdrdD=eb3D$S= zyQXQ+J6d^oXF3dfG|T$?R_6Tu1<&m1);5X~lLE-XUhbS7PN%Fq*Ye@$?R?gD9L!@V zRGrQ6druZ~x(bxew5RV7WOhC}{wBmbryzE^^((egsNtS&{fsTu*L{~B$Q?H|wCjrD zCDAc{YaJu}Rs)^7%q_#0Y!DyWP%C=JB_3{|TBda>o74#~ z#erQyUUwZAdzepbKk#hd{vXGkvTmFj=+a%smu%4prZbeeBRiC6UfMvGqCMeh$j@h) z_PKbXA$mRvl_d}Tt!e)&Rd9atN2E>c?L5eS)a^V1QP(qQQdTKsW&T$q^WPcN@c4Dy z0>>?I+ycifaNGjNEpXfd$1QN&0>>?I+ycifaNGj_wFL_Bwm|v9Q2j#6aKp`bduXW9 z&~LvKFSc~IwKoNgIrTH$>Xo9`%U7DtbCb3>gco_bn;Y;#%cdcQO3<*drBQ`L4Q;^y z-#jX#_~Oezgs`@#2M{?gqS~RB7TK*)-2x z-yRB<4>3kY#*B;zCHN9h`6Acw0wdIJz<_SP-qTnbj2Me1bxev(3QY=k;=QE}QCP1t z+!!>*MB6&XG~+d=v~+;)W(~|0SLRi%jqM#v@yb%XAvE0P9vyB*nT?xo3UqWv7P-}{ zQCYiLHVQ@EWgRUYLA-fB*mSXe!RV+-!dlrSm|Zv1ttvOz!b&}8iTm}EMVf=*A-(!bdxWB_26x%W)=u|=rO{yI5^5=( zG}kVQzscPaYHV-oXbnb#NZK?e!`@qFpIT zKPJL<(Ao}%LNZ>x`LJ&g65ONR?V;ACQcfs%^O(*KR5dD(#**!I7T-Zl6QM2BD*Nxc zkHL{m5SUf;nvpPPXHy57`6WWMZtp{CYHHlGm|>RJp?xs}_-dBhmRB~r+ej-P+w7h` zWlY44q?vwTiudX|b5ge9kfn6_8cJ!4)QqGAxNUpq!bKgO(Lmb*(#AFm8LOV_>A7yl zSMLr*pQ&I9uvUd10~C$5_WQMjvE}6+fg9^CFF3RCvTZo*8- zU4nZB?z2F9i!wbYmE1L;Zs?#X-!GhYPf>E9cjcgK%yHsHPivR%;C zIB`%(eDGZZRt`!Iyr<~tAIOW6$-vq9iez;L78@nW~ft4c-ai9c6B}A znJ0nA@E>-(K2x~XTT=hu|LX6@_04&rTT-Sfc&&oX3NBIbE(IS@@Mj7>rQmi2cPsd= zf`=6xbeIj#g=I1{t*&m8y9^`9D7XB?hGxhE3qUV2{x0XuSv(x43h;$C{T)EJio?@6 z{qnOv7=SMuxK%&(4aLuY_aS|zqKo+=Xmz*?(5sBNQ!?uIH&f9|`q1ZrPB|%6ZZVGp zEu!cZVxom_*>V1ei{>Ezi=_fJ#5@tf`xSluR7n@}Lj)gD^hTw4t&BF-D|*FMk|5@P z2yRpKpDKQpy_m-`-&YlVicDtg2kg(z?<+Z{mq|w5&mPIcUx*45_8cSm_3uDMkM*IC zP;@r@m1}5{fbR+VP@_VPgUU^fzvtmU zX+ZySF3v;00(5syjuQDc223|b-3`lfBd^aR=k`4GRe9)--xt^BFCX z^ZWkfH0Gf%MZNULza$U;gL&wW=b>-OLw`lkhZ?JteZ+bRh1jd;^Ob*!H3;bM=8^N6 z;$JgeTA*DS;*>o6J$dLq%~OxukLb@nBlFO`dFXsLm;Te&|AV0OyKMWEU+Hn7E03I& zpu1HWv%&B@{QpY%15VG3mpaX3dHDH1R44PRr1K>wP;u?d!~a$u`msFp5ty&`XU|LW z(5L62&(A}@1@scw(|xB>T!9;fJ{YgZNWz7Ren%cT_bWNNUbM6|f`6zHyIN9;3MBkR z9yyy8f2|tt#Qq18Y|X>}r#$oz^UyEI(_gvr(D~BH{_Hc`4}qeQ)u~Jp9J2#&EQBaDTJ!U;4lfk0DxU}0(q_`ul_Y=pYJD%_55+gI_kx`o3d^Z`^o9=6*mi z6Gs{9Bbhn0|Lkj>IbT}Ld}lI~oS#S(U$@Lqn2GqFV-5vh;cRHiq4a*gGl!h>@y<*P zKFSyf%7bRLPf=!~sFV8gWDZSyvonVx3``^8+&w2kei}4~ zq)n6atR}j^@zB*DG@>6c@u*2id^tCV zu1-gDX!2vOIix@UpZ?VyAioLxpML0<-_gw^M?u+|+dDGll{J=A$J={|7uA586{2P_XRQX|l! zCJ51(Q66fK28AXaVRUxk=u$=UO!1B8lt9RqW0e%C!%(524UZi$OJ)u2`Or@}9N54} z+t$W8!T-d?eR58Dv0M@tUq-=8!&wr|{Hp132In>rrm~szdjDL*2UP-0*TtpRLmIyT z)Wg_Y*7@uGb`AA@hUVAll{jNG0^DoY>GgiRh7Og(ML<<_dc7|WXAHb#HC8B_@eZHWfK968}+N^K-HqdAjOtaXI!w3-cXoQWn^eQl zFsA%bQn?nCmxkOw%}&3D4uwlYt*@l^>aXdyA|gAz-k;U*ARR30a%Y7Zyc2hJdcA+6 z;m|((x*jxng-WmM&zP@b6x^3FtfXtb6_>FJ0p_pM>-|Ixw`oQN`{vL8>d#L9*h0yu zA@_%}ufF*|hDa=dGWpl4ebEfePKr^|4 zxXL8R{A#^5-2s1Hgs{aq4xc35&KmB>#)S<#q(r0ZmqxW|9MWNbw@CB gtzyT3YaX3D>(cpZ(#Z()m;Qse{ClM#?=ZKgI7 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 wIRl5W8<( diff --git a/fastsync_server b/fastsync_server index 6f1c624a16d5cd313b58dcdd8e6298b4aac2ee0e..2540a92b30a77a050338dafe4295f32b0fe59564 100755 GIT binary patch delta 7842 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 T<%A7c(OrK@u6$&}1>-*fqtYD@ delta 7276 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 Ky8E_%ZT&yBqZBXz diff --git a/src/client.c b/src/client.c index c07f3e6..4488f85 100644 --- a/src/client.c +++ b/src/client.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ static uint64_t fast_hash_file(const char *path, uint64_t file_size) { typedef struct { int tcp_fd; int use_udp; + int use_raw; int udp_fd; struct sockaddr_in udp_server_addr; uint64_t udp_session_id; @@ -120,6 +122,391 @@ static int setup_udp(sync_ctx_t *ctx, const char *host) { return 0; } +// ── Helper: get current time in milliseconds ──────────────────────────────── +static uint64_t get_current_time_ms(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; +} + +// ── Per-block tracking for retransmission ─────────────────────────────────── +typedef struct { + uint32_t block_id; + uint64_t block_offset; + uint32_t payload_len; + char *data; // Allocated buffer with payload + uint32_t seq_num; + uint32_t retries; + uint64_t last_send_time_ms; // For timeout tracking + int acked; +} udp_block_t; + +// ── Send a single file over UDP with reliability ─────────────────────────── +static int send_file_udp(sync_ctx_t *ctx, const char *filepath, uint32_t file_id, uint64_t file_size) { + int fd = open(filepath, O_RDONLY); + if (fd < 0) { + perror("open file for UDP send"); + return -1; + } + + uint32_t total_blocks = (file_size + UDP_PAYLOAD_MAX - 1) / UDP_PAYLOAD_MAX; + + // Allocate and initialize block tracking + udp_block_t *blocks = calloc(total_blocks, sizeof(udp_block_t)); + if (!blocks) { + perror("malloc blocks"); + close(fd); + return -1; + } + + // Read file into blocks + for (uint32_t bid = 0; bid < total_blocks; bid++) { + uint64_t offset = (uint64_t)bid * UDP_PAYLOAD_MAX; + uint32_t to_read = (bid == total_blocks - 1) + ? (uint32_t)(file_size - offset) + : UDP_PAYLOAD_MAX; + + blocks[bid].block_id = bid; + blocks[bid].block_offset = offset; + blocks[bid].payload_len = to_read; + blocks[bid].seq_num = bid; // seq_num == block_id for simplicity + blocks[bid].retries = 0; + blocks[bid].acked = 0; + blocks[bid].data = malloc(to_read); + if (!blocks[bid].data) { + perror("malloc block data"); + for (uint32_t j = 0; j < bid; j++) free(blocks[j].data); + free(blocks); + close(fd); + return -1; + } + + if (lseek(fd, offset, SEEK_SET) < 0) { + perror("lseek"); + for (uint32_t j = 0; j <= bid; j++) free(blocks[j].data); + free(blocks); + close(fd); + return -1; + } + + ssize_t n = read(fd, blocks[bid].data, to_read); + if (n != (ssize_t)to_read) { + perror("read block"); + for (uint32_t j = 0; j <= bid; j++) free(blocks[j].data); + free(blocks); + close(fd); + return -1; + } + } + close(fd); + + // Send initial window of blocks + uint32_t window_base = 0; + uint32_t window_end = (total_blocks < UDP_WINDOW_SIZE) ? total_blocks : UDP_WINDOW_SIZE; + + // Send all blocks in the window + for (uint32_t bid = window_base; bid < window_end; bid++) { + udp_packet_header_t hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.magic = MAGIC_UDP_DATA; + hdr.session_id = ctx->udp_session_id; + hdr.file_id = file_id; + hdr.block_id = blocks[bid].block_id; + hdr.block_offset = blocks[bid].block_offset; + hdr.payload_len = blocks[bid].payload_len; + hdr.seq_num = blocks[bid].seq_num; + hdr.flags = (bid == total_blocks - 1) ? UDP_FLAG_LAST_BLOCK : UDP_FLAG_NONE; + + // Build packet: header + payload + char pkt[sizeof(udp_packet_header_t) + UDP_PAYLOAD_MAX]; + memcpy(pkt, &hdr, sizeof(hdr)); + memcpy(pkt + sizeof(hdr), blocks[bid].data, blocks[bid].payload_len); + + ssize_t sent = sendto(ctx->udp_fd, pkt, sizeof(hdr) + blocks[bid].payload_len, 0, + (struct sockaddr *)&ctx->udp_server_addr, + sizeof(ctx->udp_server_addr)); + if (sent < 0) { + perror("sendto UDP data"); + // Continue trying + } else { + blocks[bid].last_send_time_ms = get_current_time_ms(); + blocks[bid].retries = 1; + } + } + + // Now poll for ACKs and retransmit as needed + struct pollfd fds[1]; + fds[0].fd = ctx->tcp_fd; + fds[0].events = POLLIN; + + int completed = 0; + uint64_t start_time_ms = get_current_time_ms(); + + + while (completed < (int)total_blocks) { + // Check for timeout on un-ACKed blocks in window + uint64_t now_ms = get_current_time_ms(); + uint32_t timeout_ms = UDP_RETRANSMIT_MS * (1 + (now_ms - start_time_ms) / 5000); // Exponential backoff + if (timeout_ms > 5000) timeout_ms = 5000; // Cap at 5 seconds + + int has_timed_out = 0; + for (uint32_t bid = window_base; bid < window_end && bid < total_blocks; bid++) { + if (!blocks[bid].acked) { + if (now_ms - blocks[bid].last_send_time_ms > timeout_ms) { + if (blocks[bid].retries >= UDP_MAX_RETRIES) { + fprintf(stderr, "[UDP] Block %u exceeded max retries\n", bid); + // Give up on this block + blocks[bid].acked = 1; // Mark as "done" to avoid infinite loop + blocks[bid].retries = UDP_MAX_RETRIES + 1; + completed++; + continue; + } + + // Retransmit + udp_packet_header_t hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.magic = MAGIC_UDP_DATA; + hdr.session_id = ctx->udp_session_id; + hdr.file_id = file_id; + hdr.block_id = blocks[bid].block_id; + hdr.block_offset = blocks[bid].block_offset; + hdr.payload_len = blocks[bid].payload_len; + hdr.seq_num = blocks[bid].seq_num; + hdr.flags = UDP_FLAG_RETRANSMIT | + ((bid == total_blocks - 1) ? UDP_FLAG_LAST_BLOCK : UDP_FLAG_NONE); + + char pkt[sizeof(udp_packet_header_t) + UDP_PAYLOAD_MAX]; + memcpy(pkt, &hdr, sizeof(hdr)); + memcpy(pkt + sizeof(hdr), blocks[bid].data, blocks[bid].payload_len); + + sendto(ctx->udp_fd, pkt, sizeof(hdr) + blocks[bid].payload_len, 0, + (struct sockaddr *)&ctx->udp_server_addr, + sizeof(ctx->udp_server_addr)); + + blocks[bid].last_send_time_ms = now_ms; + blocks[bid].retries++; + has_timed_out = 1; + } + } + } + + // Poll for ACKs + int poll_timeout_ms = has_timed_out ? 10 : 50; // Short poll if we retransmitted + int poll_ret = poll(fds, 1, poll_timeout_ms); + + if (poll_ret > 0) { + // Read ACK from TCP - read entire packet at once + udp_ack_t ack_pkt; + + ssize_t n = readn(ctx->tcp_fd, &ack_pkt, sizeof(ack_pkt)); + if (n < 0) { + fprintf(stderr, "[UDP] Failed to read ACK packet (error)\n"); + break; + } + if (n != sizeof(ack_pkt)) { + // Short read - server might have sent RESP_OK or closed connection + // This can happen if all blocks are done and server sent RESP_OK + if (completed >= (int)total_blocks) { + // Transfer is complete, don't treat as error + break; + } + break; + } + + if (ack_pkt.magic == MAGIC_UDP_ACK) { + + if (ack_pkt.session_id != ctx->udp_session_id || ack_pkt.file_id != file_id) { + // Not for us, put it back (can't really, just skip) + continue; + } + + // Process cumulative ACK + uint32_t cumulative_ack = ack_pkt.ack_block_id; + + // Mark all blocks up to cumulative_ack as ACKed + for (uint32_t bid = 0; bid <= cumulative_ack && bid < total_blocks; bid++) { + if (!blocks[bid].acked) { + blocks[bid].acked = 1; + completed++; + } + } + + // Process selective ACK bitmap + for (int bmp_idx = 0; bmp_idx < 4; bmp_idx++) { + for (int bit = 0; bit < 32; bit++) { + uint32_t block_idx = bmp_idx * 32 + bit; + if (block_idx < total_blocks) { + if (ack_pkt.ack_bitmap[bmp_idx] & (1U << bit)) { + if (!blocks[block_idx].acked) { + blocks[block_idx].acked = 1; + completed++; + } + } + } + } + } + + // Check if transfer is complete + if (completed >= (int)total_blocks) { + break; // All blocks ACKed, transfer complete + } + + // Advance window: send new blocks that fit in window + // Sliding window: we can have up to UDP_WINDOW_SIZE unacked blocks + // Move window_base forward to first unacked block + while (window_base < total_blocks && blocks[window_base].acked) { + window_base++; + } + + // Send new blocks to fill the window + while (window_end < total_blocks && (window_end - window_base) < UDP_WINDOW_SIZE) { + uint32_t bid = window_end; + + udp_packet_header_t hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.magic = MAGIC_UDP_DATA; + hdr.session_id = ctx->udp_session_id; + hdr.file_id = file_id; + hdr.block_id = blocks[bid].block_id; + hdr.block_offset = blocks[bid].block_offset; + hdr.payload_len = blocks[bid].payload_len; + hdr.seq_num = blocks[bid].seq_num; + hdr.flags = (bid == total_blocks - 1) ? UDP_FLAG_LAST_BLOCK : UDP_FLAG_NONE; + + char pkt[sizeof(udp_packet_header_t) + UDP_PAYLOAD_MAX]; + memcpy(pkt, &hdr, sizeof(hdr)); + memcpy(pkt + sizeof(hdr), blocks[bid].data, blocks[bid].payload_len); + + ssize_t sent = sendto(ctx->udp_fd, pkt, sizeof(hdr) + blocks[bid].payload_len, 0, + (struct sockaddr *)&ctx->udp_server_addr, + sizeof(ctx->udp_server_addr)); + if (sent < 0) { + perror("sendto UDP data"); + } else { + blocks[bid].last_send_time_ms = now_ms; + blocks[bid].retries = 1; + window_end++; + } + } + } else { + // Some other TCP message - put it back + // We can't really put it back, so just break + fprintf(stderr, "[UDP] Unexpected TCP magic: 0x%08x during UDP transfer\n", ack_pkt.magic); + break; + } + } else if (poll_ret < 0 && errno != EINTR) { + perror("poll"); + break; + } + // If poll_ret == 0, just continue (timeout) + } + + // Clean up + for (uint32_t bid = 0; bid < total_blocks; bid++) { + free(blocks[bid].data); + } + free(blocks); + + if (completed == (int)total_blocks) { + printf("[thread] UDP sent: %s (%lu bytes, %u blocks)\n", filepath, (unsigned long)file_size, total_blocks); + atomic_fetch_add(&g_bytes_sent, file_size); + atomic_fetch_add(&g_files_sent, 1); + return 0; + } else { + fprintf(stderr, "[thread] UDP send incomplete: %d/%u blocks for %s\n", completed, total_blocks, filepath); + return -1; + } +} + +// ── Send a single file using UDP (metadata over TCP, data over UDP) ────────── +static int send_file_udp_single(sync_ctx_t *ctx, file_task_t *task) { + // Send file metadata over TCP (single-file mode, matching server's read pattern) + // Server expects: magic(4) + name_len(4) + file_size(8) + mode(4) + filename + + uint32_t name_len = strlen(task->rel_path); + uint64_t file_size = task->file_size; + uint32_t mode = 0644; // Default permissions for UDP mode + + // Send magic + uint32_t magic = MAGIC_META; + if (writen(ctx->tcp_fd, &magic, sizeof(magic)) != sizeof(magic)) { + perror("write magic"); + return -1; + } + + // Send name_len + if (writen(ctx->tcp_fd, &name_len, sizeof(name_len)) != sizeof(name_len)) { + perror("write name_len"); + return -1; + } + + // Send file_size + if (writen(ctx->tcp_fd, &file_size, sizeof(file_size)) != sizeof(file_size)) { + perror("write file_size"); + return -1; + } + + // Send mode + if (writen(ctx->tcp_fd, &mode, sizeof(mode)) != sizeof(mode)) { + perror("write mode"); + return -1; + } + + // Send filename + if (writen(ctx->tcp_fd, task->rel_path, name_len) != (ssize_t)name_len) { + perror("write filename"); + return -1; + } + + // Read server response + file_response_t resp; + + if (readn(ctx->tcp_fd, &resp, sizeof(resp)) != sizeof(resp)) { + perror("read response"); + return -1; + } + + + if (resp.response == RESP_SKIP) { + // File already exists on server with same checksum + printf("[thread] Skip (checksum match): %s\n", task->rel_path); + atomic_fetch_add(&g_files_sent, 1); + return 0; + } + + if (resp.response == RESP_ERROR) { + fprintf(stderr, "[thread] Server error for %s\n", task->rel_path); + return -1; + } + + if (resp.response != RESP_USE_UDP) { + fprintf(stderr, "[thread] Expected RESP_USE_UDP, got %u for %s\n", resp.response, task->rel_path); + return -1; + } + + uint32_t file_id = resp.file_id; + + // Now send file data over UDP with reliability + if (send_file_udp(ctx, task->full_path, file_id, task->file_size) < 0) { + fprintf(stderr, "[thread] UDP data send failed for %s\n", task->rel_path); + return -1; + } + + // Read final ACK from server + uint32_t final_ack; + if (readn(ctx->tcp_fd, &final_ack, sizeof(final_ack)) != sizeof(final_ack)) { + perror("read final ack"); + return -1; + } + + if (final_ack != RESP_OK) { + fprintf(stderr, "[thread] Server returned error ack: %u\n", final_ack); + return -1; + } + + return 0; +} + // ─── Batch file sending ──────────────────────────────────────────────────── static int send_batch_files(sync_ctx_t *ctx, file_task_t **tasks, int count) { @@ -221,6 +608,7 @@ static void *worker_thread(void *arg) { sync_ctx_t ctx; memset(&ctx, 0, sizeof(ctx)); ctx.use_udp = wa->use_udp; + ctx.use_raw = wa->use_raw; ctx.tcp_fd = socket(AF_INET, SOCK_STREAM, 0); if (ctx.tcp_fd < 0) { perror("socket"); wa->result = -1; return NULL; } @@ -244,36 +632,52 @@ static void *worker_thread(void *arg) { if (connect(ctx.tcp_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror("connect"); close(ctx.tcp_fd); wa->result = -1; return NULL; } - printf("[thread %d] TCP connected to %s:%d\n", wa->thread_id, wa->host, wa->port); + + // Send mode (SYNC or RAW) + uint32_t mode = wa->use_raw ? MODE_RAW : MODE_SYNC; + if (writen(ctx.tcp_fd, &mode, sizeof(mode)) != sizeof(mode)) { + perror("write mode"); close(ctx.tcp_fd); wa->result = -1; return NULL; } + + printf("[thread %d] TCP connected to %s:%d (mode=%s)\n", wa->thread_id, wa->host, wa->port, wa->use_raw ? "raw" : "sync"); if (wa->use_udp && setup_udp(&ctx, wa->host) < 0) { close(ctx.tcp_fd); wa->result = -1; return NULL; } - // Drain queue with batching + // Drain queue with batching (or single-file for UDP mode) file_task_t *batch[BATCH_MAX_FILES]; int batch_count = 0; file_task_t *task; while ((task = wq_pop(wa->queue)) != NULL) { - batch[batch_count++] = task; - - // If batch is full, send it - if (batch_count >= BATCH_MAX_FILES) { - if (send_batch_files(&ctx, batch, batch_count) < 0) { - fprintf(stderr, "[thread %d] Batch send failed\n", wa->thread_id); + if (ctx.use_udp) { + // UDP mode: send each file individually (single-file protocol) + if (send_file_udp_single(&ctx, task) < 0) { + fprintf(stderr, "[thread %d] UDP send failed for %s\n", wa->thread_id, task->rel_path); wa->result = -1; } - // Free all tasks in batch - for (int i = 0; i < batch_count; i++) { - free(batch[i]); + free(task); + } else { + // TCP mode: batch files + batch[batch_count++] = task; + + // If batch is full, send it + if (batch_count >= BATCH_MAX_FILES) { + if (send_batch_files(&ctx, batch, batch_count) < 0) { + fprintf(stderr, "[thread %d] Batch send failed\n", wa->thread_id); + wa->result = -1; + } + // Free all tasks in batch + for (int i = 0; i < batch_count; i++) { + free(batch[i]); + } + batch_count = 0; } - batch_count = 0; } } - // Send remaining files in partial batch - if (batch_count > 0) { + // Send remaining files in partial batch (TCP mode only) + if (!ctx.use_udp && batch_count > 0) { if (send_batch_files(&ctx, batch, batch_count) < 0) { fprintf(stderr, "[thread %d] Final batch send failed\n", wa->thread_id); wa->result = -1; @@ -300,6 +704,7 @@ static void *worker_thread(void *arg) { typedef struct { work_queue_t *queue; char base_path[1024]; + int use_raw; } scanner_arg_t; // Comparison function for sorting files by size (ascending: small files first) @@ -312,7 +717,7 @@ static int compare_files_by_size(const void *a, const void *b) { } // Collect all files recursively into a list -static int collect_files(const char *base, const char *sub, file_task_t **list, int *count, int max_files) { +static int collect_files(const char *base, const char *sub, file_task_t **list, int *count, int max_files, int use_raw) { char cur[2048]; if (sub && sub[0]) snprintf(cur, sizeof(cur), "%s/%s", base, sub); @@ -340,7 +745,7 @@ static int collect_files(const char *base, const char *sub, file_task_t **list, if (lstat(full, &st) < 0) { perror("lstat"); continue; } if (S_ISDIR(st.st_mode)) { - if (collect_files(base, rel, list, count, max_files) < 0) { + if (collect_files(base, rel, list, count, max_files, use_raw) < 0) { closedir(dir); return -1; } @@ -355,7 +760,7 @@ static int collect_files(const char *base, const char *sub, file_task_t **list, snprintf(list[*count]->full_path, sizeof(list[*count]->full_path), "%s", full); snprintf(list[*count]->rel_path, sizeof(list[*count]->rel_path), "%s", rel); list[*count]->file_size = st.st_size; - list[*count]->checksum = fast_hash_file(full, st.st_size); + list[*count]->checksum = use_raw ? 0 : fast_hash_file(full, st.st_size); list[*count]->next = NULL; (*count)++; } @@ -403,7 +808,7 @@ static void *scanner_thread(void *arg) { if (!file_list) { perror("malloc"); wq_finish(sa->queue); return NULL; } int count = 0; - if (collect_files(sa->base_path, "", file_list, &count, file_count) < 0) { + if (collect_files(sa->base_path, "", file_list, &count, file_count, sa->use_raw) < 0) { for (int i = 0; i < count; i++) free(file_list[i]); free(file_list); wq_finish(sa->queue); @@ -428,9 +833,11 @@ static void *scanner_thread(void *arg) { static void print_usage(const char *prog) { fprintf(stderr, - "Usage: %s -h [-p ] -s [-n ] [-u]\n" + "Usage: %s -h [-p ] -s [-n ] [-u] [-r]\n" " -n number of parallel TCP connections (default: 4)\n" - " -u enable UDP data channel\n", prog); + " -u enable UDP data channel\n" + " -r raw mode: send ALL files, no checksum/skip logic\n" + " (default: sync mode - only send new/updated files)\n", prog); } int main(int argc, char *argv[]) { @@ -439,14 +846,16 @@ int main(int argc, char *argv[]) { char *source = NULL; int nconn = 4; int use_udp = 0; + int use_raw = 0; // Default: sync mode int opt; - while ((opt = getopt(argc, argv, "h:p:s:n:u")) != -1) { + while ((opt = getopt(argc, argv, "h:p:s:n:ru")) != -1) { switch (opt) { case 'h': host = optarg; break; case 'p': port = atoi(optarg); break; case 's': source = optarg; break; case 'n': nconn = atoi(optarg); break; + case 'r': use_raw = 1; break; case 'u': use_udp = 1; break; default: print_usage(argv[0]); exit(EXIT_FAILURE); } @@ -459,8 +868,9 @@ int main(int argc, char *argv[]) { struct stat st; if (stat(source, &st) < 0) { perror("stat source"); exit(EXIT_FAILURE); } - printf("Starting sync: %s → %s:%d (connections=%d, udp=%s)\n", - source, host, port, nconn, use_udp ? "yes" : "no"); + printf("Starting %s: %s → %s:%d (connections=%d, udp=%s, mode=%s)\n", + use_raw ? "raw send" : "sync", source, host, port, nconn, + use_udp ? "yes" : "no", use_raw ? "raw" : "sync"); // ── Create temp dir for single files (to use directory scanning) ───────── char temp_dir[2048] = ""; @@ -492,10 +902,12 @@ int main(int argc, char *argv[]) { // ── Init shared work queue ──────────────────────────────────────────── work_queue_t queue; wq_init(&queue); + queue.use_raw = use_raw; // Set mode for scanner // ── Start scanner thread ────────────────────────────────────────────── scanner_arg_t sarg; sarg.queue = &queue; + sarg.use_raw = use_raw; snprintf(sarg.base_path, sizeof(sarg.base_path), "%s", final_source); pthread_t stid; pthread_create(&stid, NULL, scanner_thread, &sarg); @@ -516,6 +928,7 @@ int main(int argc, char *argv[]) { args[i].host = host; args[i].port = port; args[i].use_udp = use_udp; + args[i].use_raw = use_raw; args[i].result = 0; pthread_create(&tids[i], NULL, worker_thread, &args[i]); } diff --git a/src/client.o b/src/client.o index aa3bef02ecd16077232f3864b313782b36e44cf6..783c8e9b834fdda49abec0ba6cc4f31a7e77a657 100644 GIT binary patch literal 29224 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 0) { if (n >= (ssize_t)sizeof(udp_packet_header_t)) { udp_packet_header_t *hdr = (udp_packet_header_t *)pkt; + if (hdr->magic == MAGIC_UDP_DATA && hdr->session_id == session_id && hdr->file_id == active->file_id) { uint32_t bid = hdr->block_id; if (bid < active->total_blocks) { @@ -171,6 +186,60 @@ int receive_file_udp_loop(int tcp_fd, int udp_fd, uint64_t session_id, active_fi perror("pwrite file block failed"); } else { active->received_blocks[bid] = 1; + + // Update ACK tracker + ack.ack_count++; + + // Build selective ACK bitmap + for (int i = 0; i < 4; i++) { + ack.last_ack_bitmap[i] = 0; + } + for (uint32_t i = 0; i < active->total_blocks && i < 128; i++) { + if (active->received_blocks[i]) { + ack.last_ack_bitmap[i / 32] |= (1U << (i % 32)); + } + } + + // Find highest contiguous block + uint32_t highest_contiguous = 0; + while (highest_contiguous < active->total_blocks && active->received_blocks[highest_contiguous]) { + highest_contiguous++; + } + ack.last_ack_block = highest_contiguous - 1; + + // Check if all blocks received first + int all_received = 1; + for (uint32_t i = 0; i < active->total_blocks; i++) { + if (!active->received_blocks[i]) { + all_received = 0; + break; + } + } + + // Send ACK periodically (every UDP_ACK_INTERVAL packets or if window advancing) + if (ack.ack_count >= UDP_ACK_INTERVAL || bid == ack.last_ack_block) { + udp_ack_t ack_pkt; + ack_pkt.magic = MAGIC_UDP_ACK; + ack_pkt.session_id = session_id; + ack_pkt.file_id = active->file_id; + ack_pkt.ack_block_id = ack.last_ack_block; + for (int i = 0; i < 4; i++) { + ack_pkt.ack_bitmap[i] = ack.last_ack_bitmap[i]; + } + + if (writen(tcp_fd, &ack_pkt, sizeof(ack_pkt)) != sizeof(ack_pkt)) { + perror("Failed to send UDP ACK"); + return -1; + } + ack.ack_count = 0; + } + + if (all_received) { + // All blocks received - just break, don't send final ACK + // The client already knows from cumulative ACKs that all blocks are received + + break; + } } } } @@ -266,6 +335,16 @@ static void *client_thread(void *arg) { void handle_client(int sock_fd, const char *dest_dir) { printf("Client connected. Starting transfer session...\n"); + // Read mode (first 4 bytes from client) + uint32_t mode = MODE_SYNC; // Default to sync mode + ssize_t n = readn(sock_fd, &mode, sizeof(mode)); + if (n != sizeof(mode)) { + fprintf(stderr, "Failed to read mode from client\n"); + return; + } + int use_raw = (mode == MODE_RAW); + printf("Mode: %s\n", use_raw ? "RAW" : "SYNC"); + int use_udp = 0; int udp_fd = -1; struct sockaddr_in client_udp_addr; @@ -275,7 +354,7 @@ void handle_client(int sock_fd, const char *dest_dir) { while (1) { uint32_t magic = 0; // Peek or read magic - ssize_t n = readn(sock_fd, &magic, sizeof(magic)); + n = readn(sock_fd, &magic, sizeof(magic)); if (n <= 0) { if (n < 0) { perror("read magic failed"); @@ -286,12 +365,14 @@ void handle_client(int sock_fd, const char *dest_dir) { } if (magic == MAGIC_UDP_REQ) { + if (setup_udp_session(sock_fd, &udp_fd, &client_udp_addr, &udp_session_id) < 0) { fprintf(stderr, "Failed to initialize UDP session. Falling back to TCP.\n"); uint32_t resp_fail = RESP_ERROR; writen(sock_fd, &resp_fail, sizeof(resp_fail)); } else { use_udp = 1; + } continue; } @@ -346,17 +427,20 @@ void handle_client(int sock_fd, const char *dest_dir) { char target_path[2048]; snprintf(target_path, sizeof(target_path), "%s/%s", dest_dir, filename); - // Check if file already exists with same size and checksum (skip if so) - struct stat existing_st; - if (stat(target_path, &existing_st) == 0 && - (uint64_t)existing_st.st_size == meta.file_size && - S_ISREG(existing_st.st_mode)) { - // File exists with same size - check checksum - uint64_t existing_checksum = fast_hash_file(target_path, meta.file_size); - if (existing_checksum == meta.checksum) { - printf("Skip (checksum match): %s\n", target_path); - free(filename); - continue; + // In SYNC mode: check if file already exists with same size and checksum (skip if so) + // In RAW mode: always send/receive the file + if (!use_raw) { + struct stat existing_st; + if (stat(target_path, &existing_st) == 0 && + (uint64_t)existing_st.st_size == meta.file_size && + S_ISREG(existing_st.st_mode)) { + // File exists with same size - check checksum + uint64_t existing_checksum = fast_hash_file(target_path, meta.file_size); + if (existing_checksum == meta.checksum) { + printf("Skip (checksum match): %s\n", target_path); + free(filename); + continue; + } } } @@ -428,6 +512,7 @@ void handle_client(int sock_fd, const char *dest_dir) { break; } + // We received a file metadata packet (read rest of it) uint32_t name_len = 0; uint64_t file_size = 0; @@ -455,17 +540,24 @@ void handle_client(int sock_fd, const char *dest_dir) { char target_path[2048]; snprintf(target_path, sizeof(target_path), "%s/%s", dest_dir, filename); - // Ensure directories exist - if (make_path(target_path, 0755) < 0) { - perror("Failed to create parent directory path"); - file_response_t resp_err = {RESP_ERROR, 0, {0, 0}}; - writen(sock_fd, &resp_err, sizeof(resp_err)); - continue; + // Ensure parent directories exist + char *slash = strrchr(target_path, '/'); + if (slash) { + *slash = '\0'; + if (make_path(target_path, 0755) < 0) { + perror("Failed to create parent directory path"); + *slash = '/'; + file_response_t resp_err = {RESP_ERROR, 0, {0, 0}}; + writen(sock_fd, &resp_err, sizeof(resp_err)); + continue; + } + *slash = '/'; } uint32_t file_id = ++file_id_counter; if (use_udp) { + file_response_t resp_udp = {RESP_USE_UDP, file_id, {0, 0}}; if (writen(sock_fd, &resp_udp, sizeof(resp_udp)) != sizeof(resp_udp)) { perror("Failed to send file response header"); diff --git a/src/server.o b/src/server.o index d58effe313d265aa71a33db69c6cdd7a9861b83d..bf5620757df6d5d51dfd3425780fa5f965a8d0c8 100644 GIT binary patch literal 22968 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 = NULL; q->done = 0; q->error = 0; + q->use_raw = 0; pthread_mutex_init(&q->lock, NULL); pthread_cond_init(&q->cond, NULL); } diff --git a/src/utils.o b/src/utils.o index 10a37b666c0c1593a766c5d08cbf6e7943b1e375..27d06c74583f6449cacd507e54fde19597eec48a 100644 GIT binary patch delta 219 zcmcbhc0p}}Ba^;@NAnvFkK^ti7=Ylo`wtMsFX;Z@1&HC%TkYY|9s9%Z+sT(8e%ge| z1xyu;3X@+l>G63$lr$fa04h_Mtjeq-uK*VE={EN1{NUUAtwj2@4Zpkt(3oNckWrHh zn8ksbJD5YIAS#bL3ot^hf!U`p`8%^C4@5fJv$saYW3vm338SDL$bLsYfi@;jUN*;O h=Df|Ftno~YcAH?RqVl-203*;)kQ~ffiOJuY9eE(q(Vo3EDh``nSWFlN%|W(1@(HvtdGfM3 iHZ!Mh?qrQ;Vl?0UlKnk1qwwaN-06&r%9|Z|`Pl#vnLU~S