/// <summary>
 /// sends a sessioninfopacket to the client
 /// </summary>
 /// <param name="sessioninfo">the sessioninfo which is supposed to be sent</param>
 public void sendSessionInfo(headers.infotype sessioninfo)
 {
     try
     {
         sendrecv.sendStartPattern(writer);
         sendrecv.writeOnStream(writer, new byte[] { headers.packettypebytes[headers.packettype.SessionInfo] });
         sendrecv.writeOnStream(writer, new byte[] { headers.infotypebytes[sessioninfo] });
     }
     catch (Exception ex)
     {
         if (ex is ConnectionNotWorkingException)
         {
             stopAsyncListening();
             // not calling handleconnetionproblems here, because it would call it would cause a stack overflow
             //because that method calls eventually session.delete which calls this method again
         }
     }
 }
        /// <summary>
        /// continuesly waits for a new packet and calls depending on the packet type a method to handle
        /// the packet.
        /// only stops when an exception occurs or when the Asyncreadflag is set to false.
        /// </summary>
        private void reading()
        {
            try
            {
                while (Asyncreadflag)
                {
                    //wait for a new packet
                    sendrecv.waitforstartpattern(reader);
                    headers.packettype type = headers.packettypes[sendrecv.readfromstream(reader, 1)[0]]; //check the type of the packet

                    //if packet is not a sessioninfo packet forward it without looking at it
                    if (type != headers.packettype.SessionInfo)
                    {
                        Int32  datalength = BitConverter.ToInt32(sendrecv.readfromstream(reader, 4), 0);
                        byte[] data       = sendrecv.readfromstream(reader, datalength);

                        foreach (Client c in session.clients)
                        {
                            if (c.role != this.role)
                            {
                                c.forward(type, data);
                                break;
                            }
                        }
                    }
                    //if the packet is a session info packet, the information is processed by the method handlesessioninfo
                    else if (type == headers.packettype.SessionInfo)
                    {
                        headers.infotype infotype = headers.infotypes[sendrecv.readfromstream(reader, 1)[0]];
                        handlesessioninfo(infotype);
                    }
                }
            }
            catch (Exception ex)
            {
                //ignore all exceptions after the moment this thread is supposed to end
                if (Asyncreadflag)
                {
                    Console.WriteLine(
                        $"Error in async reading for {role.ToString()} in session {this.session.SessionID}: {ex.Message}");
                }
            }
        }
        /// <summary>
        /// this method handles received session info packets
        /// </summary>
        /// <param name="infotype">
        /// the type of the packet that is supposed to be processed
        /// </param>
        private void handlesessioninfo(headers.infotype infotype)
        {
            //now forward the sessioninfo to the other client in the session
            foreach (var c in session.clients)
            {
                if (infotype == headers.infotype.EndSession)
                {
                    break;                                         //don't forward endsession yet, because that is handled later
                }
                if (c.role != this.role)
                {
                    c.forward(headers.packettype.SessionInfo, new byte[] { headers.infotypebytes[infotype] });
                }
            }

            switch (infotype)
            {
            case headers.infotype.EndSession:     //moderator ended session -> delete the entire session
                Console.WriteLine("[-] SessionEnd requested");
                Asyncreadflag = false;
                sendstring("ok");     //signalize client, that the message was received, so the connection can be closed from both sides

                session.removeClient(this);
                SessionManager.Instance.delete(session);
                return;

            case headers.infotype.QuitSession:     //viewer quit session -> remove this client from the session
                sendstring("ok");
                stopAsyncListening();
                session.removeClient(this);
                break;

            case headers.infotype.RequestAccepted:     //a "get-Moderator" - Request was accepted -> toggle the roles of both clients
                foreach (Client c in session.clients)
                {
                    c.toggleRole();
                    c.sendCurrentSessionInfo();
                }
                break;
            }
        }
        /// <summary>
        /// handles a sessioninfo packet
        /// </summary>
        private void handlesessioninfo()
        {
            headers.infotype infotype = headers.infotypes[sendrecv.readfromstream(_Reader, 1)[0]]; //first get the infotype of the sessioninfopacket

            //handle the packet depending on the type of the packet
            switch (infotype)
            {
            case headers.infotype.CurrentSessionInfo:
                Int32  idlength = BitConverter.ToInt32(sendrecv.readfromstream(_Reader, 4), 0);
                string id       = Encoding.UTF8.GetString(sendrecv.readfromstream(_Reader, idlength));
                byte   role     = sendrecv.readfromstream(_Reader, 1)[0];
                this.Role = role == 0 ? delegates.Roles.Moderator : delegates.Roles.Viewer;
                SessionInfoAvailable.Invoke(id, role == 0 ? delegates.Roles.Moderator : delegates.Roles.Viewer);
                break;

            case headers.infotype.EndSession:
                stopAsyncRead();
                SessionEnded.Invoke();
                break;

            case headers.infotype.ViewerJoined:
                ViewerJoinedSession.Invoke();
                break;

            case headers.infotype.QuitSession:
                ViewerQuitSession.Invoke();
                break;

            case headers.infotype.RequestModerator:
                ModeratorRequest.Invoke();
                break;

            case headers.infotype.RequestDenied:
                RequestAnswerAvailable.Invoke(false);
                break;

            case headers.infotype.RequestAccepted:
                RequestAnswerAvailable.Invoke(true);
                break;
            }
        }