Apabila menggunakan model Socket untuk melaksanakan komunikasi rangkaian, anda perlu melalui beberapa langkah seperti mencipta Soket, mendengar port, memproses sambungan dan membaca dan menulis permintaan Sekarang mari kita lihat dengan lebih dekat operasi utama dalam langkah ini untuk membantu Kami menganalisis kekurangan dalam model Soket.
Pertama sekali, apabila kita perlu membiarkan pelayan dan pelanggan berkomunikasi, kita boleh mencipta soket mendengar (Listening Socket) yang mendengar sambungan pelanggan melalui tiga langkah berikut di bahagian pelayan:
Panggil fungsi soket untuk mencipta soket. Secara amnya, kami memanggil soket ini soket aktif
Panggil fungsi bind untuk mengikat soket aktif ke IP dan port mendengar pelayan semasa;
Panggil fungsi dengar untuk menukar soket aktif kepada soket mendengar dan mula mendengar sambungan pelanggan.listenSocket = socket(); //调用socket系统调用创建一个主动套接字 bind(listenSocket); //绑定地址和端口 listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字 while(1) { //循环监听是否有客户端连接请求到来 connSocket = accept(listenSocket);//接受客户端连接 recv(connSocket);//从客户端读取数据,只能同时处理一个客户端 send(connSocket);//给客户端返回数据,只能同时处理一个客户端 }
listenSocket = socket(); //调用socket系统调用创建一个主动套接字 bind(listenSocket); //绑定地址和端口 listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字 while(1) { //循环监听是否有客户端连接请求到来 connSocket = accept(listenSocket);//接受客户端连接 pthread_create(processData, connSocket);//创建新线程对已连接套接字进行处理 } processData(connSocket){ recv(connSocket);//从客户端读取数据,只能同时处理一个客户端 send(connSocket);//给客户端返回数据,只能同时处理一个客户端 }
int select(int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout)
关于刚才提到的第一个问题,即多路复用机制监听的套接字事件有哪些。select 函数使用三个集合,表示监听的三类事件,分别是读数据事件,写数据事件,异常事件。
我们进一步可以看到,参数 readfds、writefds 和 exceptfds 的类型是 fd_set 结构体,它主要定义部分如下所示。其中,fd_mask类型是 long int 类型的别名,__FD_SETSIZE 和 __NFDBITS 这两个宏定义的大小默认为 1024 和 32。
所以,fd_set 结构体的定义,其实就是一个 long int 类型的数组,该数组中一共有 32 个元素(1024/32=32),每个元素是 32 位(long int 类型的大小),而每一位可以用来表示一个文件描述符的状态。了解了 fd_set 结构体的定义,我们就可以回答刚才提出的第二个问题了。每个描述符集合都可以被 select 函数监听 1024 个描述符。
首先,我们在调用 select 函数前,可以先创建好传递给 select 函数的描述符集合,然后再创建监听套接字。而为了让创建的监听套接字能被 select 函数监控,我们需要把这个套接字的描述符加入到创建好的描述符集合中。
接下来,我们可以使用 select 函数并传入已创建的描述符集合作为参数。程序在调用 select 函数后,会发生阻塞。一旦 select 函数检测到有就绪的描述符,会立即终止阻塞并返回已就绪的文件描述符数。
那么此时,我们就可以在描述符集合中查找哪些描述符就绪了。然后,我们对已就绪描述符对应的套接字进行处理。比如,如果是 readfds 集合中有描述符就绪,这就表明这些就绪描述符对应的套接字上,有读事件发生,此时,我们就在该套接字上读取数据。
而因为 select 函数一次可以监听 1024 个文件描述符的状态,所以 select 函数在返回时,也可能会一次返回多个就绪的文件描述符。我们可以使用循环处理流程,对每个就绪描述符对应的套接字依次进行读写或异常处理操作。
select函数有两个不足
首先,select 函数对单个进程能监听的文件描述符数量是有限制的,它能监听的文件描述符个数由 __FD_SETSIZE 决定,默认值是 1024。
其次,当 select 函数返回后,我们需要遍历描述符集合,才能找到具体是哪些描述符就绪了。这个遍历过程会产生一定开销,从而降低程序的性能。
poll 机制的主要函数是 poll 函数,我们先来看下它的原型定义,如下所示:
int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout)
其中,参数 *__fds 是 pollfd 结构体数组,参数 __nfds 表示的是 *__fds 数组的元素个数,而 __timeout 表示 poll 函数阻塞的超时时间。
pollfd 结构体里包含了要监听的描述符,以及该描述符上要监听的事件类型。从 pollfd 结构体的定义中,我们可以看出来这一点,具体如下所示。pollfd 结构体中包含了三个成员变量 fd、events 和 revents,分别表示要监听的文件描述符、要监听的事件类型和实际发生的事件类型。
pollfd 结构体中要监听和实际发生的事件类型,是通过以下三个宏定义来表示的,分别是 POLLRDNORM、POLLWRNORM 和 POLLERR,它们分别表示可读、可写和错误事件。
了解了 poll 函数的参数后,我们来看下如何使用 poll 函数完成网络通信。这个流程主要可以分成三步:
第一步,创建 pollfd 数组和监听套接字,并进行绑定;
第二步,将监听套接字加入 pollfd 数组,并设置其监听读事件,也就是客户端的连接请求;
第三步,循环调用 poll 函数,检测 pollfd 数组中是否有就绪的文件描述符。
而在第三步的循环过程中,其处理逻辑又分成了两种情况:
如果是连接套接字就绪,这表明是有客户端连接,我们可以调用 accept 接受连接,并创建已连接套接字,并将其加入 pollfd 数组,并监听读事件;
如果是已连接套接字就绪,这表明客户端有读写请求,我们可以调用 recv/send 函数处理读写请求。
其实,和 select 函数相比,poll 函数的改进之处主要就在于,它允许一次监听超过 1024 个文件描述符。但是当调用了 poll 函数后,我们仍然需要遍历每个文件描述符,检测该描述符是否就绪,然后再进行处理。
Pertama sekali, mekanisme epoll menggunakan struktur epoll_event untuk merekodkan deskriptor fail yang akan dipantau dan jenis acara yang akan dipantau Ini adalah serupa dengan struktur pollfd yang digunakan dalam mekanisme tinjauan pendapat .
Jadi, untuk struktur epoll_event, ia mengandungi pembolehubah kesatuan epoll_data_t dan pembolehubah peristiwa jenis integer. Terdapat pembolehubah ahli fd dalam kesatuan epoll_data_t yang merekodkan deskriptor fail, dan pembolehubah peristiwa akan mengambil nilai definisi makro yang berbeza untuk mewakili jenis peristiwa yang dibimbangkan oleh deskriptor fail dalam pembolehubah epoll_data_t Sebagai contoh, beberapa perkara biasa jenis acara termasuk yang berikut Jenis ini.
EPOLLIN: Acara baca, menunjukkan bahawa soket yang sepadan dengan deskriptor fail mempunyai data untuk dibaca.
EPOLLOUT: Tulis acara, menunjukkan bahawa soket yang sepadan dengan deskriptor fail mempunyai data untuk ditulis.
EPOLLERR: Peristiwa ralat, menunjukkan bahawa deskriptor fail adalah salah untuk soket.
Apabila menggunakan fungsi pilih atau tinjauan pendapat, selepas mencipta set deskriptor fail atau tatasusunan pollfd, anda boleh menambah deskriptor fail yang perlu kami pantau pada tatasusunan.
Tetapi untuk mekanisme epoll, kita perlu memanggil fungsi epoll_create terlebih dahulu untuk mencipta contoh epoll. Contoh epoll ini mengekalkan dua struktur secara dalaman, yang merekodkan deskriptor fail untuk dipantau dan deskriptor fail sedia Untuk deskriptor fail sedia, ia akan dikembalikan kepada program pengguna untuk diproses.
Jadi, apabila kita menggunakan mekanisme epoll, kita tidak perlu merentasi untuk menanyakan deskriptor fail yang sedia seperti menggunakan pilih dan tinjauan pendapat. Oleh itu, epoll adalah lebih cekap daripada pilih dan tinjauan pendapat.
Selepas mencipta contoh epoll, kita perlu menggunakan fungsi epoll_ctl untuk menambah jenis acara mendengar pada deskriptor fail yang dipantau dan menggunakan fungsi epoll_wait untuk mendapatkan deskriptor fail sedia.
Memahami cara menggunakan fungsi epoll. Sebenarnya, ia adalah tepat kerana epoll boleh menyesuaikan bilangan deskriptor yang dipantau dan secara langsung mengembalikan deskriptor sedia bahawa apabila Redis mereka bentuk dan melaksanakan rangka kerja komunikasi rangkaian, ia berdasarkan fungsi seperti epoll_create, epoll_ctl dan epoll_wait dalam mekanisme epoll acara tulis telah dirangkumkan dan dibangunkan untuk melaksanakan rangka kerja dipacu peristiwa untuk komunikasi rangkaian, supaya walaupun Redis berjalan dalam satu utas, ia masih boleh mengendalikan akses pelanggan serentak tinggi dengan cekap.
Model Reaktor ialah model pengaturcaraan yang digunakan oleh pelayan rangkaian untuk mengendalikan permintaan IO rangkaian tinggi ciri-ciri Model:
Tiga jenis acara pemprosesan, iaitu acara sambungan, acara tulis, dan acara baca
Tiga peranan utama, iaitu reaktor, penerima dan pengendali.
Model Reaktor memperkatakan proses interaksi antara klien dan pelayan, dan ketiga-tiga jenis peristiwa ini sepadan dengan pelbagai jenis permintaan yang dicetuskan pada bahagian pelayan semasa interaksi antara klien dan pelayan menunggu acara:
Apabila pelanggan ingin berinteraksi dengan pelayan, pelanggan akan menghantar permintaan sambungan kepada pelayan untuk mewujudkan sambungan, yang sepadan dengan pautan pada pelayan Acara
Setelah sambungan diwujudkan, pelanggan akan menghantar permintaan baca kepada pelayan untuk membaca data. Apabila pelayan memproses permintaan baca, ia perlu menulis kembali data kepada klien, yang sepadan dengan acara tulis sebelah pelayan
Tidak kira pelanggan menghantar permintaan baca atau tulis kepada pelayan, pelayan Kandungan permintaan perlu dibaca daripada klien, jadi di sini, bacaan permintaan baca atau tulis sepadan dengan tiga peranan utama acara baca sebelah pelayan
:
Pertama, acara sambungan dikendalikan oleh penerima, yang bertanggungjawab untuk menerima sambungan; selepas penerima menerima sambungan, ia akan membuat pengendali untuk memproses bacaan seterusnya dan tulis acara pada sambungan rangkaian;
Kedua, acara baca dan tulis dikendalikan oleh pengendali; , peristiwa sambungan dan acara baca dan tulis akan berlaku pada masa yang sama, jadi kita perlu mempunyai peranan Khusus dalam memantau dan mengedarkan peristiwa, ini adalah peranan reaktor. Apabila terdapat permintaan sambungan, reaktor akan menyerahkan peristiwa sambungan yang dihasilkan kepada penerima untuk diproses apabila terdapat permintaan baca atau tulis, reaktor akan menyerahkan peristiwa baca dan tulis kepada pengendali untuk diproses.
Jadi, sekarang kita tahu bahawa ketiga-tiga peranan ini berinteraksi di sekitar pemantauan, pemajuan dan pemprosesan acara, bagaimanakah kita boleh melaksanakan ketiga-tiga ini apabila pengaturcaraan? Ini tidak dapat dipisahkan daripada pemanduan acara.
Pengamatan acara dilaksanakan apabila program pelayan bermula. Fungsi utamanya ialah untuk mencipta jenis acara yang perlu dipantau dan pengendali yang sepadan dengan jenis acara ini. Setelah pelayan melengkapkan pemulaan, pemulaan acara selesai dengan sewajarnya, dan program pelayan perlu memasuki gelung utama tangkapan, pengedaran dan pemprosesan acara.
Gunakan gelung sementara sebagai gelung utama. Kemudian dalam gelung utama ini, kita perlu menangkap peristiwa yang berlaku, menentukan jenis acara dan berdasarkan jenis acara, panggil pengendali acara yang dibuat semasa pemulaan untuk benar-benar mengendalikan acara.
Sebagai contoh, apabila peristiwa sambungan berlaku, program pelayan perlu memanggil fungsi pemprosesan penerima untuk membuat sambungan dengan klien. Apabila peristiwa baca berlaku, ia menunjukkan bahawa permintaan baca atau tulis telah dihantar ke pelayan Program pelayan akan memanggil fungsi pemprosesan permintaan khusus untuk membaca kandungan permintaan daripada sambungan klien, dengan itu melengkapkan pemprosesan acara baca.
Mekanisme kerja asas model Reaktor: Jenis permintaan yang berbeza daripada pelanggan akan mencetuskan tiga jenis peristiwa: sambungan, membaca dan menulis pada bahagian pelayan Pemantauan, pengedaran dan pemprosesan ketiga-tiga ini jenis peristiwa dilakukan oleh reaktor, penerima dan pengendali Ia dilengkapkan dengan tiga jenis peranan, dan kemudian ketiga-tiga jenis peranan ini akan melaksanakan interaksi dan pemprosesan peristiwa melalui rangka kerja yang didorong oleh peristiwa.
Atas ialah kandungan terperinci Apakah model yang didorong oleh peristiwa Redis?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!