c# - Telegram client updates and API requests over same session -
i'm coding custom c# telegram client starting tlsharp , modified in order support layer 54.
i want handle both receiving updates telegram servers , using api without opening separate session this.
the problem multithreaded access socket connected telegram server.
here scheme:
telegramclient <------- socket_1[(session_1)] --------> telegramserver
the problem in order receive updates telegram server use while(true) cycle schematized as:
while(true){ data_cipher = await socket_1.receive() // listen new stuff socket_1 data_plain = decrypt(data_cipher) // decrypt processupdate(data_plain) // process update }
now if want ,for example, query telegram servers list of chats in registered, have access socket_1 in order send request, , wait answer, socket_1 in listening , can't access it.
one solution use vector of request processed after update has been received, idea this:
list<request> pending_requests = new list<request>() // list of requests added thread while(true){ data_cipher = await socket_1.receive() // listen new stuff socket_1 data_plain = decrypt(data_cipher) // decrypt processupdate(data_plain) // process update if(pending_requests.count != 0){ foreach(request r in pending_requests ){ processrequest(r) } } }
this solution quite horrible, since process request after update, , no update = no request processed...
another possibility use kind of lock mechanism following scheme this:
//thread_updater //-------------------------------------------- while(true){ lock(socket_1){ data_cipher = await socket_1.receive() // listen new stuff socket_1 } data_plain = decrypt(data_cipher) // decrypt handleupdate(data_plain) // process update } -------------------------------------------- //thread_requests //-------------------------------------------- request r = new request(<stuff>); lock(socket_1){ await sendrequest(r,socket_1) } --------------------------------------------
the big problem once thread_updater takes lock, never release until update has been received... same problem before. i've tried play cancellationtasks or socket timeout, felt i'm taking wrong path.
is there elegant solution/pattern in order handle in neat way? said , don't want open 2 sessions since logically wrong ( having 2 clients in order handle receiving of updates , sending messages ).
there simpler way this.
this have done in vb.net, should work in c#.
- setup socket , connect telegram servers, listening data
- buffer data received
- process received data - decode tl types received telegram , either store/ react i.e send messages in response have received
- while listening, can send messages through same socket
sample code
region "variables"
const buffer_size = 1024 * 64 private soc socket private ep endpoint private connected boolean private efsent boolean private new autoresetevent(false) #end region #region "network" public sub connect(optional ip string = "149.154.167.40", optional port integer = 443) soc = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp) {.sendbuffersize = buffer_size, .sendtimeout = 3000} try ep = getipendpointfromhostname(ip, port) dim arg = new socketasynceventargs {.remoteendpoint = ep, .usertoken = "connect_args"} addhandler arg.completed, addressof io_handler while not connected try if not soc.connectasync(arg) io_handler(soc, arg) end if are.waitone(4000) catch ex exception thread.sleep(1000) end try end while catch ex exception log("connect: " & ex.tostring, consolecolor.red, true) end try readdata() end sub public sub disconnect() connected = false loggedin = false soc.disconnect(false) soc = nothing log("disconnect", consolecolor.darkyellow, true, true, true) end sub private sub send(m plainmessage) senddata(m.data, true) end sub private sub send(m encryptedmessage) senddata(m.data, true) end sub private sub senddata(b() byte, optional read boolean = false) b = tcppack(b) dim arg = new socketasynceventargs {.remoteendpoint = ep, .usertoken = "send_args"} addhandler arg.completed, addressof io_handler arg.setbuffer(b, 0, b.length) try if not soc.sendasync(arg) io_handler(soc, arg) end if catch ex exception log("senddata: " & ex.tostring, consolecolor.red) end try end sub private sub io_handler(sender object, e socketasynceventargs) select case e.socketerror case socketerror.success select case e.lastoperation case socketasyncoperation.connect 'a socket connect operation. connected = true log("connected " & e.connectsocket.remoteendpoint.tostring, consolecolor.green) are.set() case socketasyncoperation.disconnect connected = false raiseevent disconneted() case socketasyncoperation.receive 'a socket receive operation. if e.bytestransferred = 0 'no pending data log("the remote end has closed connection.") if connected readdata() end if connected = false loggedin = false exit sub end if handledata(e) end select case socketerror.connectionaborted raiseevent disconneted() end select end sub private function getipendpointfromhostname(hostname string, port integer) ipendpoint dim addresses = system.net.dns.gethostaddresses(hostname) if addresses.length = 0 log("unable retrieve address specified host name: " & hostname, consolecolor.red) return nothing end if return new ipendpoint(addresses(0), port) end function private function tcppack(b byte()) byte() dim = new list(of byte) dim len = cbyte(b.length / 4) if efsent = false 'tcp abridged version efsent = true a.add(&hef) end if if len >= &h7f a.add(&h7f) a.addrange(bitconverter.getbytes(len)) ' else a.add(len) end if a.addrange(b) 'data, no sequence number, no crc32 return a.toarray end function private sub readdata() dim arg = new socketasynceventargs {.remoteendpoint = ep, .usertoken = "read_args"} addhandler arg.completed, addressof io_handler dim b(buffer_size - 1) byte arg.setbuffer(b, 0, buffer_size) try if not soc.receiveasync(arg) io_handler(soc, arg) end if catch ex exception log("readmessages: " & ex.tostring, consolecolor.red) end try end sub private sub handledata(e socketasynceventargs) log("<< " & b2h(e.buffer, 0, e.bytestransferred), consolecolor.darkgray, true, logtime:=false) try dim len integer = e.buffer(0) dim start = 1 if len = &h7f len = e.buffer(1) len += e.buffer(2) << 8 len += e.buffer(3) << 16 start = 4 end if len = 4 * len dim d(len - 1) byte array.copy(e.buffer, start, d, 0, len) processresponse(d) catch ex exception end try readdata() end sub private sub processresponse(data byte()) 'process data received - identify tl types returned telegram, store / handle each required end sub #end region
Comments
Post a Comment