linux 进程 socket(docker快速入门)
大家好,如果您还对linux 进程 socket不太了解,没有关系,今天就由本站为大家分享linux 进程 socket的知识,包括docker快速入门的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!
Linux 进程间套接字通信(Socket)基础知识
姓名:罗学元 学号:21181214375 学院:广州研究院
【嵌牛导读】Linux进程间套接字通信基础
【嵌牛鼻子】Linux进程间套接字及通信介绍
【嵌牛提问】Linux进程间套接字包含哪些内容,如何实现通信
一、套接字(Socket)通信原理
套接字通信允许互联的位于不同计算机上的进程之间实现通信功能。
二、套接字的属性
套接字的特性由3个属性确定,它们分别是:域、类型和协议。
1.套接字的域
它指定套接字通信中使用的网络介质,最常见的套接字域是AF_INET,它指的是Internet网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。
另一个域AF_UNIX表示UNIX文件系统,就是文件输入/输出,它的地址就是文件名。
2.套接字类型
因特网提供了两种通信机制:流(stream)和数据报(datagram),因而套接字的类型也就分为流套接字和数据报套接字。我们主要看流套接字。
流套接字由类型SOCK_STREAM指定,它们是在AF_INET域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。
流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
与流套接字相对的是由类型SOCK_DGRAM指定的数据报套接字,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并不需要总是要建立和维持一个连接。
3.套接字协议
只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。通常只需要使用默认值。
三、套接字地址
每个套接字都有其自己的地址格式,对于AF_UNIX域套接字来说,它的地址由结构sockaddr_un来描述,该结构定义在头文件
struct sockaddr_un{
sa_family_t sun_family; //AF_UNIX,它是一个短整型
char sum_path[]; //路径名
};
对于AF_INET域套接字来说,它的地址结构由sockaddr_in来描述,它至少包括以下几个成员:
struct sockaddr_in{
short int sin_family; //AN_INET
unsigned short int sin_port; //端口号
struct in_addr sin_addr; //IP地址
}
而in_addr被定义为:
struct in_addr{
unsigned long int s_addr;
}
四、基于流套接字的客户/服务器的工作流程
使用socket进行进程通信的进程采用的客户/服务器系统是如何工作的呢?
1.服务器端
首先,服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
接下来,服务器进程会给套接字起个名字,我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
然后,系统调用listen来创建一个队列,并将其用于存放来自客户的进入连接。
最后,服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接。
2.客户端
基于socket的客户端比服务器端简单。同样,客户应用程序首先调用socket来创建一个未命名的套接字,然后讲服务器的命名套接字作为一个地址来调用connect与服务器建立连接。
一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信。
Linux支持最大的SOCKET连接数量是多少
$ ulimit-n
1024
这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个文件中还得除去每个进程必然打开的标准输入,标准输出,标准错误,服务器监听 socket,进程间通讯的unix域socket等文件,那么剩下的可用于客户端socket连接的文件数就只有大概1024-10=1014个左右。也就是说缺省情况下,基于Linux的通讯程序最多允许同时1014个TCP并发连接。
超级NB的黑科技,实现socket进程间迁移!
今天我们将探讨一项令人瞩目的技术:实现socket句柄在进程间的迁移。在服务器领域,我们经常需要管理大量的server实例。这些实例需要处理数以万计的连接和繁重的网络请求。如果能轻而易举地控制这些连接,像操作乐高积木一样,那么在软件升级时,我们就可以先启动一个新版本的进程,然后将老进程的socket逐个转移到新进程中,实现无缝升级。这项技术,Facebook已成功实践,被称为SocketTakeover。虽然这项技术很有用,但在搜索引擎中却难以找到相关资料。也许大家都在忙于研究茴香豆的茴字怎么写,无暇他顾。今天,就由我为大家揭开这项技术的神秘面纱,让你的吹牛资本更上一层楼。这项功能,是基于Linux底层系统调用函数sendmsg()和recvmsg()实现的。
首先,我们需要了解c语言网络编程的基础。通常,我们会通过listen函数注册监听地址,然后使用accept函数接收新连接。例如:
```c
int listen_fd= socket(addr->ss_family, SOCK_STREAM, 0);
...
bind(listen_fd,(struct sockaddr*)addr, addrlen);
...
int accept_fd= accept(fd,(struct sockaddr*)&addr,&addrlen);
```
我们的目标,是将listen_fd从一个进程传递到另一个进程。这需要一个通道,而在Linux上,那就是Unix Domain Sockets(UDS)。
UDS在Linux上表现为一个文件,它允许进程通过这个文件进行数据传输,而无需经过网卡等物理设备,因此速度极快。今天,我们不关注它的速度,而是关注它的实用性。通过bind函数,我们可以在UDS文件上接收连接,就像在端口上接收连接一样。
```c
struct sockaddr_un addr;
char*path="/tmp/xjjdog.sock";
int fd;
fd= socket(AF_UNIX, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family= AF_UNIX;
strncpy(addr.sun_path, path, strlen(path));
addrlen= sizeof(addr.sun_family)+ strlen(path);
err= bind(fd,(struct sockaddr*)&addr, addrlen);
...
accept_fd= accept(fd,(struct sockaddr*)&addr,&addrlen);
```
其他进程,现在可以通过端口或UDS文件连接到我们的服务。
接下来,我们来看迁移的过程。关键在于sendmsg函数的第二个参数——msghdr结构体。msghdr结构体包含了一些iov(输入/输出缓冲区)和控制信息。其中,控制信息可以通过CMSG_FIRSTHDR宏获取,它指向cmsghdr结构体。cmsghdr结构体中有一个cmsg_type成员,用于指定控制信息的类型。在这里,我们关注SCM_RIGHTS类型,它允许我们从一个进程发送文件句柄到另一个进程。
```c
struct msghdr msg;
...
struct cmsghdr* cmsg= CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level= SOL_SOCKET;
cmsg->cmsg_type= SCM_RIGHTS;
//设置cmsg_data为socketfd列表
int* fds=(int*)CMSG_DATA(cmsg);
```
通过sendmsg函数,socket句柄就被发送到另一个进程了。
接收方,则需要使用recvmsg函数,并解析cmsghdr结构体,从cmsg_data中获取句柄列表。实际上,socket句柄在进程中只是一个引用,真正的句柄在内核中。迁移,不过是将一个引用从内核中移除,然后添加到另一个进程中。
对于普通fd,我们需要调用与原始连接到来时相同的代码逻辑。因此,迁移过程包括:迁移listener fd到新进程并开始监听,迁移大量的socket连接,并逐步在新进程中还原它们。完成后,老进程就可以安全地关闭了。
这是一项具有革命性的技术,已经在一些主流应用中得到应用。例如,HAProxy用于4层网络的负载均衡;Envoy和Istio默认的数据平面软件,也使用类似技术完成热重启。在service mesh的推进过程中,proxy的替换也会使用这种技术,如SOFA。虽然golang和C语言因为API暴露较好,实现起来较为容易,但在Java中,由于缺少这种为Linux定制的API,实现起来就有不少困难。这种技术非常适合无状态的proxy服务,如果有状态的服务尝试使用,可能会有安全风险,但也可以尝试应用在一些中间件上。无论如何,这种技术的出现,肯定会令windowsserver用户羡慕不已。