Soket, juga dikenali sebagai soket, ialah sejenis kaedah komunikasi antara proses (IPC) Linux Ia bukan sahaja boleh mencapai komunikasi silang proses dalam hos yang sama, tetapi juga mencapai komunikasi silang proses antara yang berbeza hos komunikasi.
Persekitaran pengendalian tutorial ini: sistem linux5.9.8, komputer Dell G3.
Maksud asal soket ialah "soket". Dalam bidang komunikasi komputer, soket diterjemahkan sebagai "soket". Melalui konvensyen soket, komputer boleh menerima data daripada komputer lain dan juga boleh menghantar data ke komputer lain.
Soket dalam Linux
Soket ialah komunikasi silang proses Linux (IPC, Inter Process Communication, untuk butiran, sila rujuk: Ringkasan Kaedah komunikasi antara proses Linux ) kaedah. Berbanding dengan kaedah IPC lain, kelebihan Socket ialah ia bukan sahaja boleh mencapai komunikasi silang proses dalam hos yang sama, tetapi juga mencapai komunikasi silang proses antara hos yang berbeza. Mengikut domain komunikasi yang berbeza, ia boleh dibahagikan kepada dua jenis: soket domain Unix dan soket domain Internet.
1. Soket domain Internet
Soket domain Internet digunakan untuk melaksanakan komunikasi antara proses pada hos yang berbeza, soket yang kami rujuk merujuk kepada internet. (Melainkan dinyatakan sebaliknya di bawah, soket merujuk kepada soket domain internet.)
Untuk mencapai komunikasi silang proses antara hos yang berbeza, masalah pertama yang perlu diselesaikan ialah cara mengenal pasti proses secara unik. Kami tahu bahawa setiap proses pada hos mempunyai pid yang unik, dan pid boleh menyelesaikan masalah mengenal pasti proses komunikasi silang proses pada hos yang sama. Tetapi jika kedua-dua proses tidak berada pada hos yang sama, pid mungkin diulang, jadi ia tidak boleh digunakan dalam senario ini. Kami tahu bahawa hos boleh dikunci secara unik melalui IP hos, dan program boleh didapati melalui port Untuk komunikasi antara proses, kami juga perlu mengetahui protokol yang digunakan untuk komunikasi. Dengan cara ini, gabungan "protokol port IP" boleh mengenal pasti secara unik proses pada hos dalam rangkaian. Ini juga merupakan parameter utama untuk menjana soket.
Selepas setiap proses mempunyai pengecam unik, langkah seterusnya ialah komunikasi. Komunikasi adalah masalah satu tamparan Di mana terdapat program penghantar, terdapat program penerima, dan Socket boleh dianggap sebagai titik akhir dalam sambungan komunikasi antara kedua-dua hujungnya. dan Soket penghantar Hantar maklumat ini ke Soket hujung penerima, dan akhirnya maklumat ini dihantar ke hujung penerima. Mengenai cara maklumat pergi dari Soket penghantaran ke Soket penerima, itu adalah sesuatu yang perlu dibimbangkan oleh sistem pengendalian dan susunan rangkaian. Kami tidak perlu mengetahui butirannya. Seperti yang ditunjukkan dalam rajah di bawah:
Untuk mengekalkan sambungan antara kedua-dua hujungnya, Soket kami tidak mencukupi untuk mempunyai pengecam uniknya sendiri. Ia juga memerlukannya pengecam unik pihak yang satu lagi, jadi satu yang disebut di atas Sebenarnya hanya terdapat separuh daripada Soket penghantaran dan penerimaan Soket yang lengkap hendaklah terdiri daripada tatasusunan 5 dimensi yang terdiri daripada [protokol, alamat tempatan, port tempatan, alamat jauh, port jauh] . Sebagai contoh, Soket hujung penghantaran ialah [tcp, menghantar IP hujung, menghantar port akhir, menerima IP hujung, menerima port akhir], maka Soket hujung penerima ialah [tcp, menerima IP hujung, menerima port akhir, menghantar IP akhir, menghantar port akhir].
Mari kita gunakan analogi untuk mendalami pemahaman kita Contohnya, jika saya menghantar WeChat kepada anda untuk menghubungi anda, kami adalah prosesnya, pelanggan WeChat ialah Socket, dan ID WeChat adalah unik kami. pengecam bagi Tencent Kami tidak perlu mengambil berat tentang butiran cara menghantar mesej WeChat yang saya hantar ke WeChat anda. Untuk mengekalkan hubungan antara kami berdua, tidak cukup untuk Socket kami hanya mempunyai pelanggan WeChat Kami juga perlu menambah rakan, supaya kami boleh mencari satu sama lain melalui senarai rakan pelanggan WeChat saya ialah Soket lengkap saya Dan saya dalam senarai rakan pelanggan WeChat anda ialah Soket lengkap anda. Harap saya tidak menjatuhkan awak. . .
Soket boleh dibahagikan kepada 3 jenis mengikut protokol komunikasi yang berbeza: soket aliran (SOCK_STREAM), soket datagram (SOCK_DGRAM) dan soket mentah.
Soket penstriman (SOCK_STREAM): Soket yang paling biasa, menggunakan protokol TCP, menyediakan aliran komunikasi berorientasikan sambungan yang boleh dipercayai. Pastikan penghantaran data adalah betul dan berurutan. Digunakan dalam sambungan jauh Telnet, perkhidmatan WWW, dsb.
Soket Data (SOCK_DGRAM): Menggunakan protokol UDP untuk menyediakan perkhidmatan tanpa sambungan Data dihantar melalui mesej bebas, yang tidak teratur dan tidak dijamin boleh dipercayai. Aplikasi yang menggunakan UDP mesti mempunyai protokol mereka sendiri untuk mengesahkan data.
Soket mentah: membenarkan akses terus kepada protokol lapisan rendah seperti IP atau ICMP, terutamanya digunakan untuk menguji pelaksanaan protokol rangkaian baharu. Soket mentah digunakan terutamanya untuk pembangunan beberapa protokol dan boleh melakukan operasi peringkat rendah. Ia berkuasa, tetapi ia tidak semudah digunakan seperti dua soket yang diperkenalkan di atas, dan program biasa tidak melibatkan soket asal.
Proses kerja soket ditunjukkan dalam rajah di bawah (mengambil soket penstriman sebagai contoh, proses soket datagram adalah berbeza, anda boleh merujuk kepada: Apakah soket (Soket)) : Pelayan bermula dahulu, mewujudkan soket dengan memanggil socket(), kemudian memanggil bind() untuk mengaitkan soket dengan alamat rangkaian tempatan, dan kemudian memanggil listen() untuk menyediakan soket untuk mendengar Dan nyatakan panjangnya minta baris gilir, dan kemudian panggil accept() untuk menerima sambungan. Selepas mewujudkan soket, pelanggan boleh memanggil connect() untuk mewujudkan sambungan dengan pelayan. Setelah sambungan diwujudkan, data boleh dihantar dan diterima antara klien dan pelayan dengan memanggil read() dan write(). Akhir sekali, selepas penghantaran data selesai, kedua-dua pihak memanggil close() untuk menutup soket.
Proses di atas boleh diringkaskan dari perspektif sambungan TCP seperti yang ditunjukkan dalam rajah Anda boleh melihat bahawa jabat tangan tiga hala TCP mewakili proses mewujudkan sambungan Soket . Selepas sambungan diwujudkan, anda boleh membaca , teruskan untuk menghantar data antara satu sama lain, dan empat kali terakhir dilambai untuk memutuskan sambungan dan memadam Soket.
2. Soket domain Unix
Soket domain Unix juga dipanggil soket IPC (komunikasi antara proses), digunakan Untuk mencapai komunikasi antara proses pada hos yang sama. Soket pada asalnya direka untuk komunikasi rangkaian, tetapi kemudiannya mekanisme IPC telah dibangunkan berdasarkan rangka kerja soket, iaitu soket domain UNIX. Walaupun soket rangkaian juga boleh digunakan untuk komunikasi antara proses pada hos yang sama (melalui alamat gelung balik 127.0.0.1), soket domain UNIX lebih cekap untuk IPC: mereka tidak perlu melalui susunan protokol rangkaian, pembungkusan dan pembongkaran, dan pengiraan semak , mengekalkan nombor jujukan dan respons, dsb., hanya salin data lapisan aplikasi dari satu proses ke proses yang lain. Ini kerana mekanisme IPC adalah komunikasi yang boleh dipercayai, manakala protokol rangkaian direka untuk komunikasi yang tidak boleh dipercayai.
Soket domain UNIX adalah dupleks penuh, antara muka API mempunyai semantik yang kaya, dan mempunyai kelebihan yang jelas berbanding mekanisme IPC lain Ia telah menjadi mekanisme IPC yang paling banyak digunakan, seperti antara pelayan X Window dan program GUI It berkomunikasi melalui soket domain UNIX. Soket domain Unix ialah komponen standard POSIX, jadi jangan keliru dengan namanya, sistem Linux juga menyokongnya.
Pelajar yang mengenali Docker harus tahu bahawa daemon Docker memantau fail docker.sock. Laluan lalai fail docker.sock ini ialah /var/run/docker.sock. Ini akan diperkenalkan secara terperinci dalam sesi praktikal kemudian.
Amalan Soket
Cara terbaik untuk mempelajari pengaturcaraan dengan baik ialah dengan berlatih. Seterusnya, mari kita gunakan komunikasi Soket dan perhatikan fail Socket
1 Amalan soket domain Internet
Sekarang kita akan menggunakan soket untuk menulis pelayan, kerana saya C. Saya kurang pengalaman bahasa, jadi saya memilih untuk berlatih dengan GoLang di sini. Fungsi pelayan adalah sangat mudah, iaitu, ia mendengar port 1208 Apabila ia menerima ping input, ia mengembalikan pong Apabila ia menerima gema xxx, ia mengembalikan xxx sambungan. Artikel rujukan kod untuk socket-server.go: Menggunakan Go for Socket Programming |. Seperti berikut:
package main import ( "fmt" "net" "strings" ) func connHandler(c net.Conn) { if c == nil { return } buf := make([]byte, 4096) for { cnt, err := c.Read(buf) if err != nil || cnt == 0 { c.Close() break } inStr := strings.TrimSpace(string(buf[0:cnt])) inputs := strings.Split(inStr, " ") switch inputs[0] { case "ping": c.Write([]byte("pong\n")) case "echo": echoStr := strings.Join(inputs[1:], " ") + "\n" c.Write([]byte(echoStr)) case "quit": c.Close() break default: fmt.Printf("Unsupported command: %s\n", inputs[0]) } } fmt.Printf("Connection from %v closed. \n", c.RemoteAddr()) } func main() { server, err := net.Listen("tcp", ":1208") if err != nil { fmt.Printf("Fail to start server, %s\n", err) } fmt.Println("Server Started ...") for { conn, err := server.Accept() if err != nil { fmt.Printf("Fail to connect, %s\n", err) break } go connHandler(conn) } }
Dalam sistem seperti Unix di mana semuanya adalah fail, soket yang dihasilkan oleh proses diwakili oleh fail soket, dan proses merealisasikan penghantaran mesej dengan membaca dan menulis kandungan ke fail soket. Dalam sistem Linux, fail soket biasanya berada di bawah laluan fail /proc/pid/fd/. Mulakan pelayan soket kami dan mari kita lihat fail soket yang sepadan. Mulakan pelayan dahulu:
# go run socket-server.go Server Started ...
Buka tetingkap lain Kami mula-mula menyemak pid proses pelayan Anda boleh menggunakan perintah lsof atau netstat:
# lsof -i :1208 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME socket-se 20007 root 3u IPv6 470314 0t0 TCP *:1208 (LISTEN) # netstat -tupan | grep 1208 tcp6 0 0 :::1208 :::* LISTEN 20007/socket-server
Anda boleh melihatnya. pid pelayan kami ialah 20007, seterusnya mari kita semak soket yang dipantau oleh pelayan:
# ls -l /proc/20007/fd total 0 lrwx------ 1 root root 64 Sep 11 07:15 0 -> /dev/pts/0 lrwx------ 1 root root 64 Sep 11 07:15 1 -> /dev/pts/0 lrwx------ 1 root root 64 Sep 11 07:15 2 -> /dev/pts/0 lrwx------ 1 root root 64 Sep 11 07:15 3 -> 'socket:[470314]' lrwx------ 1 root root 64 Sep 11 07:15 4 -> 'anon_inode:[eventpoll]'
Anda boleh melihat bahawa /proc/20007/fd/3 ialah fail pautan yang menunjuk ke soket:[470314], ini ialah soket sisi pelayan. Permulaan socket-server telah melalui tiga proses: socket() --> bind() --> listen().
Kami tahu bahawa komunikasi soket memerlukan sepasang soket: bahagian pelayan dan bahagian pelanggan. Sekarang mari kita buka tetingkap lain dan gunakan telnet untuk memulakan klien pada mesin yang sama dengan soket-server Mari kita lihat pada soket di sebelah klien:
# telnet localhost 1208 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'.
Teruskan periksa deskriptor fail yang dibuka. oleh port pelayan;
# lsof -i :1208 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME socket-se 20007 root 3u IPv6 470314 0t0 TCP *:1208 (LISTEN) socket-se 20007 root 5u IPv6 473748 0t0 TCP localhost:1208->localhost:51090 (ESTABLISHED) telnet 20375 ubuntu 3u IPv4 473747 0t0 TCP localhost:51090->localhost:1208 (ESTABLISHED)
Kami mendapati bahawa berbanding keputusan sebelumnya, terdapat 2 lagi keputusan ini ialah:
*:1208 (. LISTEN) ialah fail soket mendengar daripada pelayan Namanya, pid proses ialah 20007
localhost:1208->localhost:51090 (ESTABLISHED) ialah soket baharu yang ditubuhkan oleh pelayan untuk pelanggan, bertanggungjawab untuk berkomunikasi dengan pelanggan, dan proses kepunyaannya The pid ialah 20007
localhost:51090->localhost:1208 (ESTABLISHED) ialah soket baharu ditubuhkan oleh pelanggan untuk pelayan Ia bertanggungjawab untuk berkomunikasi dengan pelayan Proses pid ialah 20375
在/proc/pid/fd/
文件路径下可以看到server和client新建的socket,这里不做赘述。从第3条结果我们可以看出,前2条socket,LISTEN socket和新建的ESTABLISHED socket都属于server进程,对于每条链接server进程都会创建一个新的socket去链接client,这条socket的源IP和源端口为server的IP和端口,目的IP和目的端口是client的IP和端口。相应的client也创建一条新的socket,该socket的源IP和源端口与目的IP和目的端口恰好与server创建的socket相反,client的端口为一个主机随机分配的高位端口。
从上面的结果我们可以回答一个问题 “服务端socket.accept后,会产生新端口吗”? 答案是不会。server的监听端口不会变,server为client创建的新的socket的端口也不会变,在本例中都是1208。这难到不会出现端口冲突吗?当然不会,我们知道socket是通过5维数组[协议,本地IP,本地端口,远程IP,远程端口] 来唯一确定的。socket: *:1208 (LISTEN)和socket: localhost:1208->localhost:51090 (ESTABLISHED)是不同的socket 。那这个LISTEN socket有什么用呢?我的理解是当收到请求连接的数据包,比如TCP的SYN请求,那么这个连接会被LISTEN socket接收,进行accept处理。如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用ESTABLISHED套接字通过recv或者read函数到缓冲区里面去取指定的数据,这样就可以保证响应会发送到正确的客户端。
上面提到客户端主机会为发起连接的进程分配一个随机端口去创建一个socket,而server的进程则会为每个连接创建一个新的socket。因此对于客户端而言,由于端口最多只有65535个,其中还有1024个是不准用户程序用的,那么最多只能有64512个并发连接。对于服务端而言,并发连接的总量受到一个进程能够打开的文件句柄数的限制,因为socket也是文件的一种,每个socket都有一个文件描述符(FD,file descriptor),进程每创建一个socket都会打开一个文件句柄。该上限可以通过ulimt -n查看,通过增加ulimit可以增加server的并发连接上限。本例的server机器的ulimit为:
# ulimit -n 1024
上面讲了半天服务端与客户端的socket创建,现在我们来看看服务端与客户端的socket通信。还记得我们的server可以响应3个命令吗,分别是ping,echo和quit,我们来试试:
# telnet localhost 1208 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. ping pong echo Hello,socket Hello,socket quit Connection closed by foreign host.
我们可以看到client与server通过socket的通信。
到此为止,我们来总结下从telnet发起连接,到客户端发出ping,服务端响应pong,到最后客户端quit,连接断开的整个过程:
telnet发起向localhost:1208发起连接请求;
server通过socket: TCP *:1208 (LISTEN)收到请求数据包,进行accept处理;
server返回socket信息给客户端,客户端收到server socket信息,为客户端进程分配一个随机端口51090,然后创建socket: TCP localhost:51090->localhost:1208 来连接服务端;
服务端进程创建一个新的socket: TCP localhost:1208->localhost:51090来连接客户端;
客户端发出ping,ping数据包send到socket: TCP localhost:51090->localhost:1208 ;
服务端通过socket: TCP localhost:1208->localhost:51090收到ping数据包,返回pong,pong数据包又通过原路返回到客户端 ,完成一次通信。
客户端进程发起quit请求,通过上述相同的socket路径到达服务端后,服务端切断连接,服务端删除socket: TCP localhost:1208->localhost:51090释放文件句柄;客户端删除 socket: TCP localhost:51090->localhost:1208,释放端口 51090。
在上述过程中,socket到socket之间还要经过操作系统,网络栈等过程,这里就不做细致描述。
2. Unix domain socket实践
我们知道docker使用的是client-server架构,用户通过docker client输入命令,client将命令转达给docker daemon去执行。docker daemon会监听一个unix domain socket来与其他进程通信,默认路径为/var/run/docker.sock。我们来看看这个文件:
# ls -l /var/run/docker.sock srw-rw---- 1 root docker 0 Aug 31 01:19 /var/run/docker.sock
可以看到它的Linux文件类型是“s”,也就是socket。通过这个socket,我们可以直接调用docker daemon的API进行操作,接下来我们通过docker.sock调用API来运行一个nginx容器,相当于在docker client上执行:
# docker run nginx
与在docker client上一行命令搞定不同的是,通过API的形式运行容器需要2步:创建容器和启动容器。
1. 创建nginx容器,我们使用curl命令调用docker API,通过--unix-socket /var/run/docker.sock指定Unix domain socket。首先调用/containers/create,并传入参数指定镜像为nginx,如下:
# curl -XPOST --unix-socket /var/run/docker.sock -d '{"Image":"nginx"}' -H 'Content-Type: application/json' http://localhost/containers/create {"Id":"67bfc390d58f7ba9ac808d3fc948a5d4e29395e94288a7588ec3523af6806e1a","Warnings":[]}
2. 启动容器,通过上一步创建容器返回的容器id,我们来启动这个nginx:
# curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/67bfc390d58f7ba9ac808d3fc948a5d4e29395e94288a7588ec3523af6806e1a/start
# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 67bfc390d58f nginx "/docker-entrypoint.…" About a minute ago Up 7 seconds 80/tcp romantic_heisenberg
至此,通过Unix domain socket我们实现了客户端进程curl与服务端进程docker daemon间的通信,并成功地调用了docker API运行了一个nginx container。
值得注意的是,在连接服务端的Unix domain socket的时候,我们直接指定的是服务端的socket文件。而在使用Internet domain socket的时候,我们指定的是服务端的IP地址和端口号。
总结
Socket是Linux跨进程通信方式的一种。它不仅仅可以做到同一台主机内跨进程通信,它还可以做到不同主机间的跨进程通信。根据通信域的不同可以划分成2种:Unix domain socket 和 Internet domain socket。
Internet domain socket根据通信协议划分成3种:流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM)及原始套接字
一个完整的Socket的组成应该是由[协议,本地地址,本地端口,远程地址,远程端口]组成的一个5维数组。
相关推荐:《Linux视频教程》
Atas ialah kandungan terperinci apa itu soket linux. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!