进程间通信有几种稳定、有效的的方式几乎被所有的操作系统采用:共享内存(Shared Memory)、管道(Pipe)、Unix Domain Socket和 RPC(Remote Procedure Calls 远程调用),本文主要介绍UDS和RPC.
Socket和HTTP的区别?
Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层 。而http是个应用层的协议,它实际上也建立在TCP协议之上(HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力)。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。
Tip:顺便说下TCP和UDP的区别。TCP提供可靠的通信传输,类似于打电话,需要等待另一方的接听,才能进行真正的通讯;而UDP则不是可靠的,类似发短信,只将信息发出,至于对方有没收到,这个就不关心了。
socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIXDomain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。
Unix Domain Socket,我们因为学习TCP/IP协议才接触到Socket。Socket在网络通信领域广泛应用 被称为Network Socket;Socket在同一机器上的进程间通信也是完全能够胜任的,但是执行效率不理想。因此出现了Unix Domain Socket专门针对单机内的进程间通信。(Network Socket与UDS的区别:前者是以TCP/IP协议栈为基础的,需要分包、重组等一系列操作,而后者的实现机制上不是依赖这些协议的)
UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。
使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。
UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
mysql的主机和客户机在同一host(物理服务器)上的时候,mysql.sock就是使用unix domain socket做为通讯协议的载体,它比tcp快。
socket文件还在一般情况下通过socket文件是可以免密码登录的,所以以上的登录也可以这样直接登录。
#"S"的选项和"--socket="是一样的
mysql -S /tmp/mysql3307.sock
另外一个例子:
#include
#include
#include
#include
#include
#include
int main()
{
/* delete the socket file */
unlink("server_socket");
/* create a socket */
int server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un server_addr;
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, "server_socket");
/* bind with the local file */
bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
/* listen */
listen(server_sockfd, 5);
char ch;
int client_sockfd;
struct sockaddr_un client_addr;
socklen_t len = sizeof(client_addr);
while(1)
{
printf("server waiting:\n");
/* accept a connection */
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
/* exchange data */
read(client_sockfd, &ch, 1);
printf("get char from client: %c\n", ch);
++ch;
write(client_sockfd, &ch, 1);
/* close the socket */
close(client_sockfd);
}
return 0;
}
#include
#include
#include
#include
#include
#include
int main()
{
/* create a socket */
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, "server_socket");
/* connect to the server */
int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
if(result == -1)
{
perror("connect failed: ");
exit(1);
}
/* exchange data */
char ch = 'A';
write(sockfd, &ch, 1);
read(sockfd, &ch, 1);
printf("get char from server: %c\n", ch);
/* close the socket */
close(sockfd);
return 0;
}
我们调用socket函数创建一个socket:
int socket(int domain, int type, int protocol)
domain:指定socket所属的域,常用的是AF_UNIX或AF_INET
AF_UNIX表示以文件方式创建socket,AF_INET表示以端口方式创建socket(我们会在后面详细讲解AF_INET)
type:指定socket的类型,可以是SOCK_STREAM或SOCK_DGRAM
SOCK_STREAM表示创建一个有序的,可靠的,面向连接的socket,因此如果我们要使用TCP,就应该指定为SOCK_STREAM
SOCK_DGRAM表示创建一个不可靠的,无连接的socket,因此如果我们要使用UDP,就应该指定为SOCK_DGRAM
protocol:指定socket的协议类型,我们一般指定为0表示由第一第二两个参数自动选择。
socket()函数返回新创建的socket,出错则返回-1
2.地址格式:
常用的有两种socket域:AF_UNIX或AF_INET,因此就有两种地址格式:sockaddr_un和sockaddr_in,分别定义如下:
struct sockaddr_un
{
sa_family_t sun_family; /* AF_UNIX */
char sun_path[]; /* pathname */
}
struct sockaddr_in
{
short int sin_family; /* AF_INET */
unsigned short int sin_port; /* port number */
struct in_addr sin_addr; /* internet address */
}
struct in_addr
{
unsigned long int s_addr;
}
从上面的定义我们可以看出,sun_path存放socket的本地文件名,sin_addr存放socket的ip地址,sin_port存放socket的端口号。
RPC