Tentang siswa kelas satu, pembelajaran jarak jauh, dan pemrograman asynchronous

gambar



Saya seorang downshifter. Kebetulan selama tiga tahun terakhir saya dan istri menikmati pemandangan pedesaan di luar jendela, udara segar dan kicau burung. Kenyamanan di dalam rumah, internet optik dari provider lokal, power supply yang kuat dan tidak pernah terputus, dan covid yang tiba-tiba muncul, mendadak membuat ide pindah dari kota metropolis tidak begitu aneh.



Sementara saya sangat antusias terlibat dalam pengembangan web, di suatu tempat di latar belakang, istri saya secara berkala mengeluh tentang masalah memilih sekolah untuk anak saya. Dan kemudian (tiba-tiba) anak itu tumbuh besar dan pertanyaan sekolah itu berdiri tegak. Oke, waktunya telah tiba. Mari kita cari tahu bersama, apa yang salah dengan sistem pendidikan di bekas 1/6 negara, dan apa yang dapat kita lakukan?



Saya akan meninggalkan metode pengajaran tatap muka tradisional di luar cakupan artikel ini. Saya hanya akan mengatakan bahwa sekolah biasa memiliki keuntungan dan kerugian serius yang tak terbantahkan, yang, omong-omong, isolasi diri paksa baru-baru ini ditambahkan. Di sini kita melihat pilihan pendidikan jarak dan keluarga yang, karena berbagai alasan, akhir-akhir ini menarik semakin banyak orang tua.



Untuk lebih jelasnya: pembelajaran jarak jauh menyiratkan kelas-kelas di sekolah reguler menggunakan "teknologi pembelajaran jarak jauh" (DOT), dan pendidikan keluarga berarti secara sukarela meninggalkan sekolah dan belajar hanya oleh keluarga (sebenarnya, ini adalah eksternship lama yang baik). Namun, bagaimanapun, anak tersebut harus terikat pada salah satu sekolah yang tersedia, setidaknya untuk lulus sertifikasi menengah.



Dan sekarang beberapa pengamatan dari kehidupan. Dengan transfer paksa ke pembelajaran jarak jauh anak-anak yang telah belajar di sekolah biasa, semuanya menyedihkan. Anak-anak sekolah menganggap anugerah takdir ini sebagai semacam liburan, orang tua tidak terbiasa mengikuti disiplin selama kelas, dan akibatnya, prestasi akademis secara keseluruhan pasti jatuh.



Dengan siswa kelas satu, terutama dalam kasus bentuk keluarga, orang tua, mungkin, memiliki kesempatan untuk menempatkan anak "di atas rel" dengan menggunakan minat alami dan pengaruh kebaruan. Bagi saya pribadi, mencapai kemerdekaan adalah tugas utama. Duduk dan mengerjakan pekerjaan rumah dengan seorang anak, saya menganggap ketinggian kebodohantidak sepenuhnya masuk akal. Tentu saja, jika Anda ingin anak-anak Anda mencapai sesuatu dalam hidup dan tidak bergantung pada Anda. Oleh karena itu, saya ingin tujuan saya adalah mengajar seorang anak untuk belajar, mengajukan pertanyaan dengan benar dan, secara umum, berpikir dengan kepalanya sendiri.



Langsung ke intinya. Memilih sekolah umum



Mungkin, saya lebih menyukai pendidikan keluarga karena adanya kesempatan untuk memilih program dan jadwal pelatihan. Dan secara fisik Anda lebih jarang bersekolah. Tetapi Anda harus memilih sekolah umum, berbicara dengan direktur tentang penempatan anak dan menerima pesanan untuk masuk ke kelas satu di akhir musim dingin sehingga tidak ada kejutan di bulan September. Meskipun, dari sudut pandang hukum, undang-undang tentang pendidikan tampaknya tidak memerlukan pengesahan tahunan, "tenggat waktu", menurut pengalaman saya, adalah motivator yang sangat baik, jadi biarlah ada pengesahan. Tidak mungkin sekolah mana pun akan menerima kami dengan tangan terbuka, tetapi saya yakin kami akan dapat menemukan pilihan yang layak di kota terdekat.



Memilih kurikulum



Kami memilih dengan tepat. Mencoba membuat program sendiri tanpa pendidikan khusus tidaklah masuk akal. Meskipun ada sumber daya pendidikan pemerintah seperti Sekolah Elektronik Rusia ( NES ) dan Sekolah Elektronik Moskow ( MES ), yang secara teori akan mencukupi, tetapi ... Kedua opsi tersebut menyediakan rencana pelajaran, video, tes, dan tutorial. Apa yang tidak dapat saya temukan adalah buku teks itu sendiri, bahkan untuk kurikulum wajib.



Dan di sini yang paling penting hilang: komunikasi. Mengajar seorang anak dengan menunjukkan video tanpa akhir dan memaksanya untuk mencentang tes tidak akan berhasil. Ini berarti Anda perlu melakukan pelajaran sepenuhnya secara mandiri, atau memilih salah satu sekolah online.



Memilih sekolah online



Kami hampir kembali ke tempat kami memulai. Kontrol jarak jauh? Oke, mari kita lihat dia lebih dekat. Bagaimana Anda bisa mengatur proses pendidikan dari jarak jauh? Ini menimbulkan banyak pertanyaan, saya hanya akan mengangkat yang kunci:



* Komunikasi langsung. Apa yang ditawarkan sekolah? Skype, Tim terbaik. Pelajaran Skype? Betulkah? Kalau tidak salah, tahun 2020. Buka beberapa jendela dengan tombol multi-warna yang indah di depan siswa kelas satu dan tunggu sampai dia tidak menekannya, tetapi akan dengan patuh mendengarkan paman atau bibi yang membosankan selama setengah hari? Saya belum pernah melihat anak-anak seperti itu. Dan kau?



* Pekerjaan rumah. Lebih tepatnya, bagaimana hal itu sampai ke guru untuk diuji? Sebenarnya, ini adalah pertanyaan yang sangat sulit, bahkan mungkin tidak terpecahkan secara prinsip. Opsi yang ada:



  1. , . --, , , , - .

  2. . , - .

  3. . , .

  4. . , , , , ? . , , .

  5. . , , . , , , . , . . , , , , . .



* Perkiraan. Jelas, nilai yang diberikan dalam pelajaran dan ketika memeriksa pekerjaan rumah harus dimasukkan ke dalam buku harian elektronik yang tersedia untuk orang tua. Dan mereka sampai di sana. Tapi tidak langsung. Saya bertanya kepada anak-anak yang lebih tua yang lulus dari salah satu sekolah menengah bergengsi sebagai kubah emas (ironisnya, dengan bias informasional), mengapa demikian? Jawabannya, sejujurnya, mengejutkan saya. Ternyata para guru menuliskan nilai di selembar kertas, dan setelah pelajaran mereka mengarahkan mereka ke buku harian elektronik di portal negara bagian ini. Dan ini sementara Tesla Elon Musk membajak luasnya ruang ...



Oke, saatnya melakukan sedikit penelitian teknis dan memeriksa apakah ada alasan obyektif untuk keadaan ini?



Mari kita tentukan persyaratan untuk platform pembelajaran ideal hipotetis. Faktanya, semuanya sederhana: anak-anak harus tetap dalam pelajaran, fokus pada apa yang dikatakan dan ditunjukkan oleh guru, menjawab pertanyaan jika perlu dan mengangkat tangan jika diinginkan. Pada dasarnya, kami menginginkan jendela layar penuh dengan aliran dari kamera guru, presentasi, atau papan tulis. Cara termudah untuk melakukannya adalah dengan menggunakan teknologi WebRTC(komunikasi waktu nyata, komunikasi waktu nyata). Hal ini berfungsi di browser yang lebih atau kurang modern, tidak memerlukan pembelian peralatan tambahan dan, terlebih lagi, menyediakan koneksi berkualitas baik. Dan ya, standar ini memerlukan pemrograman asinkron setidaknya karena metode JS yang diperlukan navigator.mediaDevices.getUserMedia () mengembalikan sebuah janji . Semuanya tampak jelas, saya mulai menerapkannya.



Penyimpangan lirik tentang memilih kerangka kerja
, «» JavaScript , . jQuery. , JS :



//  
element = $(selector);
element = document.querySelector(selector);

//    
element2 = element.find(selector2);
element2 = element.querySelector(selector2);

//  
element.hide();  //   display: none
element.classList.add('hidden');

      
      





, CSS «hidden», , opacity transition, fadeIn/fadeOut CSS. , JS !



//   onClick
element.click(e => { ... });
element.onclick = (e) => { ...  }

//  
element.toggleClass(class_name);
element.classList.toggle(class_name);

//  div
div = $("<div>");
div = document.createElement("div");

//   div  element
// (  ,   )
element.append(div);
element.append(div);

      
      





. .. , JS , . , , «» JS !



WebRTC dirancang untuk komunikasi langsung antar browser, menggunakan teknologi point-to-point (p2p). Namun, untuk membangun koneksi ini, browser harus saling menginformasikan maksud mereka untuk berkomunikasi. Ini membutuhkan server alarm .



Contoh penerapan dasar obrolan video sederhana menggunakan topologi "tautan penuh"
'use strict';

(function () {
    const selfView = document.querySelector('#self-view'),
        remoteMaster = document.querySelector('#remote-master'),
        remoteSlaves = document.querySelector('#remote-slaves');

    let localStream,
        selfStream = null,
        socket = null,
        selfId = null,
        connections = {};

    // ***********************
    // UserMedia & DOM methods
    // ***********************

    const init = async () => {
        try {
            let stream = await navigator.mediaDevices.getUserMedia({
                audio: true, video: {
                    width: { max: 640 }, height: { max: 480 }
                }
            });
            localStream = stream;

            selfStream = new MediaStream();

            stream.getVideoTracks().forEach(track => {
                selfStream.addTrack(track, stream); // track.kind == 'video'
            });
            selfView.querySelector('video').srcObject = selfStream;

        } catch (e) {
            document.querySelector('#self-view').innerHTML =
                '<i>     </i>';
            console.error('Local stream not found: ', e);
        }
        wsInit();
    }

    const createRemoteView = (id, username) => {
        let iDiv = document.querySelector('#pc' + id);
        if (!iDiv) {
            iDiv = document.createElement('div');
            iDiv.className = 'remote-view';
            iDiv.id = 'pc' + id;

            let iVideo = document.createElement('video');
            iVideo.setAttribute('autoplay', 'true');
            iVideo.setAttribute('playsinline', 'true');

            let iLabel = document.createElement('span');

            iDiv.append(iVideo);
            iDiv.append(iLabel);

            if (!remoteMaster.querySelector('video')) {
                remoteMaster.append(iDiv);
                iLabel.textContent = '';
            } else {
                remoteSlaves.append(iDiv);
                iLabel.textContent = username;
            }
            remoteMaster.style.removeProperty('display');
        }
    }

    // *******************************
    // Signaling (Web Socket) methods
    // *******************************

    const wsInit = () => {
        socket = new WebSocket(SIGNALING_SERVER_URL);

        socket.onopen = function (e) {
            log('[socket open]  ');
        }

        socket.onmessage = function (event) {
            log('[socket message]    ', event);

            wsHandle(event.data);
        }

        socket.onclose = function (event) {
            if (event.wasClean) {
                log('[close]   , ' +
                    `=${event.code} =${event.reason}`);
            } else {
                log('[socket close]  ', event);
            }
            clearInterval(socket.timer);
        }

        socket.onerror = function (error) {
            logError('[socket error]', error);
        }

        socket.timer = setInterval(() => {
            socket.send('heartbeat');
        }, 10000);
    }

    const wsHandle = async (data) => {
        if (!data) {
            return;
        }
        try {
            data = JSON.parse(data);
        } catch (e) {
            return;
        }

        switch (data.type) {
            case 'handshake':
                selfId = data.uid;
                if (!Object.keys(data.users).length) {
                    createRemoteView(selfId, '');
                    remoteMaster.querySelector('video').srcObject =
                        selfStream;
                    selfView.remove();
                    break;
                } else {
                    selfView.style.removeProperty('display');
                }
                for (let id in data.users) {
                    await pcCreate(id, data.users[id]);
                }
                break;
            case 'offer':
                await wsHandleOffer(data);
                break;
            case 'answer':
                await wsHandleAnswer(data)
                break;
            case 'candidate':
                await wsHandleICECandidate(data);
                break;
            default:
                break;
        }
    }

    const wsHandleOffer = async (data) => {
        let pc = null;

        if (!connections[data.src]) {
            await pcCreate(data.src, data.username);
        }

        pc = connections[data.src].pc;

        // We need to set the remote description to the received SDP offer
        // so that our local WebRTC layer knows how to talk to the caller.
        let desc = new RTCSessionDescription(data.sdp);

        pc.setRemoteDescription(desc).catch(error => {
            logError('handleOffer', error);
        });

        await pc.setLocalDescription(await pc.createAnswer());

        wsSend({
            type: 'answer',
            target: data.src,
            sdp: pc.localDescription
        });

        connections[data.src].pc = pc; // ???
    }

    const wsHandleAnswer = async (data) => {
        log('*** Call recipient has accepted our call, answer:', data);

        let pc = connections[data.src].pc;

        // Configure the remote description,
        // which is the SDP payload in our 'answer' message.

        let desc = new RTCSessionDescription(data.sdp);
        await pc.setRemoteDescription(desc).catch((error) => {
            logError('handleAnswer', error);
        });
    }

    const wsHandleICECandidate = async (data) => {
        let pc = connections[data.src].pc;

        let candidate = new RTCIceCandidate(data.candidate);

        log('*** Adding received ICE candidate', candidate);

        pc.addIceCandidate(candidate).catch(error => {
            logError('handleICECandidate', error);
        });
    }

    const wsSend = (data) => {
        if (socket.readyState !== WebSocket.OPEN) {
            return;
        }
        socket.send(JSON.stringify(data));
    }

    // ***********************
    // Peer Connection methods
    // ***********************

    const pcCreate = async (id, username) => {
        if (connections[id]) {
            return;
        }
        try {
            let pc = new RTCPeerConnection(PC_CONFIG);

            pc.onicecandidate = (event) =>
                pcOnIceCandidate(event, id);
            pc.oniceconnectionstatechange = (event) =>
                pcOnIceConnectionStateChange(event, id);
            pc.onsignalingstatechange =  (event) =>
                pcOnSignalingStateChangeEvent(event, id);
            pc.onnegotiationneeded = (event) =>
                pcOnNegotiationNeeded(event, id);
            pc.ontrack = (event) =>
                pcOnTrack(event, id);

            connections[id] = {
                pc: pc,
                username: username
            }

            if (localStream) {
                try {
                    localStream.getTracks().forEach(
                        (track) => connections[id].pc.addTransceiver(track, {
                            streams: [localStream]
                        })
                    );
                } catch (err) {
                    logError(err);
                }
            } else {
                // Start negotiation to listen remote stream only
                pcOnNegotiationNeeded(null, id);
            }
            createRemoteView(id, username);
        } catch (error) {
            logError('Peer: Connection failed', error);
        }
    }

    const pcOnTrack = (event, id) => {
        let iVideo = document.querySelector('#pc' + id + ' video');
        iVideo.srcObject = event.streams[0];
    }

    const pcOnIceCandidate = (event, id) => {
        let pc = connections[id].pc;

        if (event.candidate && pc.remoteDescription) {
            log('*** Outgoing ICE candidate: ' + event.candidate);
            wsSend({
                type: 'candidate',
                target: id,
                candidate: event.candidate
            });
        }
    }

    const pcOnNegotiationNeeded = async (event, id) => {
        let pc = connections[id].pc;
        try {
            const offer = await pc.createOffer();

            // If the connection hasn't yet achieved the "stable" state,
            // return to the caller. Another negotiationneeded event
            // will be fired when the state stabilizes.
            if (pc.signalingState != 'stable') {
                return;
            }

            // Establish the offer as the local peer's current
            // description.
            await pc.setLocalDescription(offer);

            // Send the offer to the remote peer.
            wsSend({
                type: 'offer',
                target: id,
                sdp: pc.localDescription
            });
        } catch(err) {
            logError('*** The following error occurred while handling' +
                ' the negotiationneeded event:', err);
        };
    }

    const pcOnIceConnectionStateChange = (event, id) => {
        let pc = connections[id].pc;
        switch (pc.iceConnectionState) {
            case 'closed':
            case 'failed':
            case 'disconnected':
                pcClose(id);
                break;
        }
    }

    const pcOnSignalingStateChangeEvent = (event, id) => {
        let pc = connections[id].pc;

        log('*** WebRTC signaling state changed to: ' + pc.signalingState);

        switch (pc.signalingState) {
            case 'closed':
                pcClose(id);
                break;
        }
    }

    const pcClose = (id) => {
        let remoteView = document.querySelector('#pc' + id);

        if (connections[id]) {
            let pc = connections[id].pc;
            pc.close();
            delete connections[id];
        }
        if (remoteView) {
            remoteView.remove();
        }
    }

    // *******
    // Helpers
    // *******

    const log = (msg, data) => {
        if (!data) {
            data = ''
        }
        console.log(msg, data);
    }

    const logError = (msg, data) => {
        if (!data) {
            data = ''
        }
        console.error(msg, data);
    }

    init();
})();

      
      







Server pensinyalan dibuat pada kerangka kerja aiohttp Python dan merupakan "tampilan" sederhana yang secara sepele mem-proxy permintaan WebRTC. Koneksi ke server dalam contoh ini dilakukan melalui soket web . Nah, sebagai tambahan, data obrolan teks sederhana dikirimkan melalui saluran pensinyalan.



Contoh implementasi server pensinyalan
import json
from aiohttp.web import WebSocketResponse, Response
from aiohttp import WSMsgType
from uuid import uuid1
from lib.views import BaseView


class WebSocket(BaseView):
    """ Process WS connections """

    async def get(self):
        username = self.request['current_user'].firstname or ''

        room_id = self.request.match_info.get('room_id')

        if room_id != 'test_room' and
            self.request['current_user'].is_anonymous:
            self.raise_error('forbidden')  # @TODO: send 4000

        if (self.request.headers.get('connection', '').lower() != 'upgrade' or
            self.request.headers.get('upgrade', '').lower() != 'websocket'):
            return Response(text=self.request.path)  # ???

        self.ws = WebSocketResponse()
        await self.ws.prepare(self.request)

        self.uid = str(uuid1())

        if room_id not in self.request.app['web_sockets']:
            self.request.app['web_sockets'][room_id] = {}

        self.room = self.request.app['web_sockets'][room_id]

        users = {}
        for id, data in self.room.items():
            users[id] = data['name']

        ip = self.request.headers.get(
            'X-FORWARDED-FOR',
            self.request.headers.get('X-REAL-IP',
            self.request.remote))

        msg = {
            'type': 'handshake',
            'uid': str(self.uid),
            'users': users, 'ip': ip}
        await self.ws.send_str(json.dumps(msg, ensure_ascii=False))

        self.room[self.uid] = {'name': username, 'ws': self.ws}

        try:
            async for msg in self.ws:
                if msg.type == WSMsgType.TEXT:
                    if msg.data == 'heartbeat':
                        print('---heartbeat---')
                        continue

                    try:
                        msg_data = json.loads(msg.data)

                        if 'target' not in msg_data or
                            msg_data['target'] not in self.room:
                            continue

                        msg_data['src'] = self.uid

                        if 'type' in msg_data and 'target' in msg_data:
                            if msg_data['type'] == 'offer':
                                msg_data['username'] = username
                        else:
                            print('INVALID DATA:', msg_data)
                    except Exception as e:
                        print('INVALID JSON', e, msg)

                    try:
                        await self.room[msg_data['target']]['ws'].send_json(
                            msg_data);
                    except Exception as e:
                        if 'target' in msg_data:
                            self.room.pop(msg_data['target'])

        finally:
            self.room.pop(self.uid)

        return self.ws

      
      







Teknologi WebRTC, selain komunikasi video, memungkinkan Anda memberi izin browser untuk menangkap konten tampilan atau aplikasi terpisah, yang sangat diperlukan saat melakukan pelajaran, webinar, atau presentasi online. Bagus, mari kita gunakan.



Saya begitu terbawa oleh kemungkinan komunikasi video modern sehingga saya hampir lupa tentang subjek terpenting di kelas - papan tulis interaktif. Namun, penerapan dasarnya sangat sepele sehingga saya tidak akan membebani artikel ini dengannya. Kami hanya menambahkan kanvas , mendengarkan peristiwa gerakan mouse onmousemove (ontouchmove untuk tablet) dan mengirim koordinat yang diterima ke semua titik yang terhubung melalui server pensinyalan yang sama.



Menguji papan tulis interaktif Anda



Di sini Anda membutuhkan tablet, digitizer, dan anak yang hidup. Pada saat yang sama, kami akan memeriksa kemungkinan mendigitalkan masukan tulisan tangan.



Untuk memulainya, saya mengambil tablet Galaxy Tab lama di Android 4.4, stylus buatan sendiri dan copybook pertama yang saya temukan sebagai latar belakang kanvas. Saya tidak menginstal program tambahan. Hasilnya membuat saya putus asa: tablet saya sama sekali tidak cocok untuk menulis! Artinya, untuk menggerakkan jari Anda di atasnya tidak masalah, tetapi memasukkan stylus ke dalam garis bentuk huruf, bahkan yang sebesar gambar di bawah, sudah menjadi masalah. Plus, gadget mulai tumpul dalam proses menggambar, akibatnya garis menjadi putus. Selain itu, saya tidak bisa membuat anak itu tidak meletakkan pergelangan tangannya di layar, yang meninggalkan pulas tambahan di tangan, dan tablet itu sendiri mulai semakin melambat. Intinya: tablet biasa untuk menulis di papan tulis tidak cocok. Kemampuan maksimalnya adalah dengan menggerakkan jari Anda melintasi layar angka yang agak besar. Tetapi sudah terlambat untuk menawarkan ini kepada anak-anak sekolah.



Oke, ini murni penelitian teoritis, bukan? Kemudian kami mengambil digitizer (alias tablet grafis) format Wacom Bamboo A8, dan mengawasi si anak.



Perhatikan bahwa subjek tes saya yang berusia enam tahun menerima laptop dengan pena gambar untuk pertama kalinya dalam hidupnya. Kami membutuhkan waktu sekitar sepuluh menit untuk mendapatkan keterampilan dasar menggunakan pena, dan sudah di pelajaran kedua anak menggunakan tablet dengan cukup percaya diri, terhapus sendiri dari papan, menggambar wajah, bunga, anjing kami, dan bahkan mulai menyodok tombol yang tersedia di epsilon, sekaligus mengajukan pertanyaan seperti "Mengapa mereka mengangkat tangan di sekolah?" Tetapi hasilnya selalu meninggalkan banyak hal yang harus diinginkan. Faktanya adalah bahwa desainer dan seniman memaksimalkan fragmen gambar untuk menggambar elemen, yang membuat garis menjadi akurat. Di sini kita akan melihat keseluruhan papan, dalam skala 1: 1. Di sini dan orang dewasa tidak akan jatuh ke dalam barisan. Inilah yang kami dapatkan:



gambar



Putusan akhir: tidak ada tulisan tangan yang keluar dari pertanyaan. Dan jika kita ingin "membantu" anak-anak kita, kita perlu mencapai ini sendiri, di atas kertas, sekolah tidak akan membantu dalam hal ini.



Saya harus mengatakan bahwa anak itu melakukan semua eksperimen saya dengan antusias dan, terlebih lagi, sejak itu mengikuti saya dengan ekor dan meminta saya untuk "menghidupkan resepnya". Baiklah, keterampilan yang diperoleh akan berguna baginya, hanya untuk tujuan yang sama sekali berbeda.



Bagaimanapun, sebagai hasil eksperimen, saya benar-benar mendapatkan MVP - produk yang layak minimum, hampircocok untuk pelajaran online, dengan konferensi video / audio, layar bersama, papan tulis interaktif, obrolan teks sederhana dan tombol angkat tangan. Ini jika anak tiba-tiba tidak memiliki mikrofon. Ya, ini terjadi, terutama di antara anak-anak yang belum belajar.



Tapi sayangnya di tong madu ini, ada beberapa sendok tar.



Menguji WebRTC



Sendok nomor 1. Karena komunikasi video kami menggunakan koneksi langsung antar klien, langkah pertama adalah menguji skalabilitas solusi semacam itu. Untuk pengujian, saya mengambil laptop lama dengan i5-3230M dual-core, dan mulai menghubungkan klien dengan kamera web yang dinonaktifkan ke sana, yaitu, meniru mode satu-ke-banyak:



gambar



Seperti yang Anda lihat, laptop eksperimental dapat menyiarkan lebih atau kurang nyaman ke lima klien ( pada beban CPU dalam 60%). Dan ini asalkan resolusi aliran video keluar dikurangi menjadi 720p (640x480px) dan kecepatan bingkai menjadi 15 fps. Pada prinsipnya, tidak terlalu buruk, tetapi ketika menghubungkan kelas yang terdiri dari beberapa lusin siswa, Anda harus meninggalkan "jaring lengkap" untuk beralih ke kaskade, yaitu, masing-masing dari lima klien pertama memproksikan aliran ke lima berikutnya, dan seterusnya.



Sendok nomor 2.Untuk membuat koneksi interaktif langsung (ICE) antar klien, mereka perlu melewati firewall dan NAT. Untuk melakukan ini, WebRTC menggunakan server STUN , yang memberi tahu klien tentang parameter koneksi eksternal. Diyakini bahwa dalam banyak kasus ini sudah cukup. Tapi aku langsung "beruntung":



Seperti yang Anda lihat, debugger mengeluh tentang ketidakmungkinan koneksi ICE dan membutuhkan server TURN untuk dihubungkan, yaitu relai. Dan ini sudah MAHAL. Server pensinyalan sederhana sangat diperlukan. Kesimpulan - Anda harus melewatkan semua aliran melalui server media.



Server Media



Untuk pengujian, saya menggunakan aiortc . Perkembangan yang menarik, memungkinkan Anda untuk menghubungkan browser langsung ke server melalui WebRTC. Sinyal terpisah tidak diperlukan, Anda dapat menggunakan saluran data dari koneksi itu sendiri. Ini berfungsi, semua poin pengujian saya terhubung tanpa masalah. Tapi masalah dengan performa. Gema sederhana dari aliran video / audio dengan batas 720p dan 15fps yang sama memakan 50% dari CPU virtual saya pada pengujian VDS. Selain itu, jika beban ditingkatkan menjadi 100%, aliran video tidak memiliki waktu untuk diturunkan ke klien dan mulai menyumbat memori, yang pada akhirnya menyebabkan penghentian server. Jelas, Python yang kami suka gunakan untuk pemrosesan I / O tidak terlalu terikat dengan CPU. Kita harus mencari solusi yang lebih terspesialisasi, misalnya Janusatau Jitsy .



Bagaimanapun, solusi lengkap akan membutuhkan server khusus, menurut perkiraan saya, dengan tarif 1 inti per kamar (kelas). Ini sudah membutuhkan biaya dan melampaui pengujian sederhana, jadi pada titik ini saya akan mempertimbangkan tahap pertama dari penelitian saya yang telah selesai.



kesimpulan



1. Sederhananya, aneh melihat petunjuk unduhan dan tautan untuk mendaftar dalam program mantan musuh potensial nomor 1 (di sini tentang Tim Microsoft) di portal resmi Federasi Rusia . Dan ini di era sanksi dan substitusi impor.

Tidak, secara pribadi saya untuk persahabatan orang-orang dan, secara umum, semua jenis toleransi, tetapi apakah benar-benar hanya saya dari "integrasi" seperti itu sehingga rambut saya berdiri tegak? Apakah tidak ada perkembangan kita?



2. Integrasi MES / NES dengan sekolah. Sebenarnya, para pengembang MES itu hebat, mereka bahkan melakukan integrasi dengan Yandex.tutor. Bagaimana dengan penilaian waktu nyata selama pelajaran, kapan akan ada API? Atau apakah saya tidak menyadari sesuatu?



3. Saat memilih jarak atau bentuk pendidikan keluarga, Anda harus jujur ​​pada diri sendiri: Anda tidak akan dapat mengalihkan tanggung jawab atas pendidikan anak Anda ke sekolah. Semua pekerjaan melaksanakan pelajaran (dalam hal pendidikan keluarga), menjaga disiplin dan pengaturan diri (dalam hal apapun) sepenuhnya menjadi tanggung jawab orang tua. Anda perlu menyadari hal ini dan meluangkan waktu untuk kelas. Namun, dalam keluarga besar hal ini seharusnya tidak menjadi masalah.



4. Saya tidak akan menyertakan link ke sekolah online yang dipilih di sini, jangan sampai dianggap sebagai iklan. Saya hanya akan mengatakan bahwa kami telah memilih sekolah swasta dengan kisaran harga menengah. Bagaimanapun, hasil akhir akan tergantung pada anak dan kami akan menerimanya paling lambat September.



Atau apakah masuk akal untuk membawa perkembangan yang dimulai di sini ke kesimpulan logisnya dan mengatur sekolah Anda sendiri? Bagaimana menurut anda? Apakah ada orang yang berpikiran sama dengan pengetahuan dan pengalaman khusus di bidang pendidikan?



Tautan yang berguna: Perpustakaan MES Sekolah Elektronik

Rusia Sekolah

Elektronik Moskow

Untuk

Pengembang



All Articles