进程通信————本地套接字

来源:互联网 发布:外贸大数据软件 编辑:程序博客网 时间:2024/05/16 05:36

 唉,从去年开始就和那该死的开发包作战,现在还没完全拿下!有些人和组织自以为自己水平很高,方法很好,弄出一些极其复杂难以掌握的开发包,实在令人郁闷和不齿!还是OSEK组织好,轻量级的API让人倍感亲切,颇有当年周郎“谈笑间,强虏灰飞烟灭”的意思,看来David投对胎了!哈哈,扯了这么多,再来写上一段,估计郁闷烦躁的心情就会变成“春风得意马蹄疾,一日看尽长安花”了!

本地套接字的学名叫做文件系统套接字,是UNIX/Linux套接字家族中的一员,AF_UNIX是它的大名,呵呵,是不是带着点王者的气息啊?不光如此,它的使用也十分简单,下面我们就来看一看。

在程序中使用本地套接字,首先要包含支持它的头文件,下面的语句给出了包含方法:
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<unistd.h>
其中,前三个头文件是使用本地套接字所必需的定义性文件,后两个头文件是对本地套接字进行I/O操作时可能用到的文件,一般情况下,五个文件都要包含在程序中。

头文件准备好了,就可以创建套接字并使用了,创建套接字需要使用socket系统调用,其原型如下:
int socket(int domain, int type, int protocol);
其中,domain参数指定协议族,对于本地套接字来说,其值须被置为AF_UNIX枚举值;type参数指定套接字类型,protocol参数指定具体协议;type参数可被设置为SOCK_STREAM(流式套接字)或SOCK_DGRAM(数据报式套接字),protocol字段应被设置为0;其返回值为生成的套接字描述符。

对于本地套接字来说,流式套接字(SOCK_STREAM)是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道;数据报式套接字(SOCK_DGRAM)相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,但由于其在本地通信,不通过外界网络,这些情况出现的概率很小。

SOCK_STREAM流式本地套接字的通信双方均需要具有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用struct sockaddr_un类型的变量,将相应字段赋值,再将其绑定在创建的服务器套接字上,绑定要使用bind系统调用,其原形如下:
int bind(int socket, const struct sockaddr *address, size_t address_len);
其中socket表示服务器端的套接字描述符,address表示需要绑定的本地地址,是一个struct sockaddr_un类型的变量,address_len表示该本地地址的字节长度。实现服务器端地址指定功能的代码如下(假设服务器端已经通过上文所述的socket系统调用创建了套接字,server_sockfd为其套接字描述符):

struct sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "Server Socket");
bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));

客户端的本地地址不用显式指定,只需能连接到服务器端即可,因此,客户端的struct sockaddr_un类型变量需要根据服务器的设置情况来设置,代码如下(假设客户端已经通过上文所述的socket系统调用创建了套接字,client_sockfd为其套接字描述符):

struct sockaddr_un client_address;
client_address.sun_family = AF_UNIX;
strcpy(client_address.sun_path, "Server Socket");

服务器端套接字创建完毕并赋予本地地址值(名称,本例中为Server Socket)后,需要进行监听,等待客户端连接并处理请求,监听使用listen系统调用,接受客户端连接使用accept系统调用,它们的原形如下:
int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);
其中socket表示服务器端的套接字描述符;backlog表示排队连接队列的长度(若有多个客户端同时连接,则需要进行排队);address表示当前连接客户端的本地地址,该参数为输出参数,是客户端传递过来的关于自身的信息;address_len表示当前连接客户端本地地址的字节长度,这个参数既是输入参数,又是输出参数。实现监听、接受和处理的代码如下:

#define MAX_CONNECTION_NUMBER 10
int server_client_length, server_client_sockfd;
struct sockaddr_un server_client_address;
listen(server_sockfd, MAX_CONNECTION_NUMBER);
while(1)
{
    // ...... (some process code)
    server_client_length = sizeof(server_client_address);
    server_client_sockfd = accept(server_sockfd, (struct sockaddr*)&server_client_address, &server_client_length);
    // ...... (some process code)
}

这里使用死循环的原因是服务器是一个不断提供服务的实体,它需要不间断的进行监听、接受并处理连接,本例中,每个连接只能进行串行处理,即一个连接处理完后,才能进行后续连接的处理。如果想要多个连接并发处理,则需要创建线程,将每个连接交给相应的线程并发处理(关于多进程/线程程序设计,以后将另辟专题讨论)。

客户端套接字创建完毕并赋予本地地址值后,需要连接到服务器端进行通信,让服务器端为其提供处理服务。对于SOCK_STREAM类型的流式套接字,需要客户端与服务器之间进行连接方可使用。连接要使用connect系统调用,其原形为
int connect(int socket, const struct sockaddr *address, size_t address_len);
其中socket为客户端的套接字描述符,address表示当前客户端的本地地址,是一个struct sockaddr_un类型的变量,address_len表示本地地址的字节长度。实现连接的代码如下:

connect(client_sockfd, (struct sockaddr*)&client_address, sizeof(client_address));

无论客户端还是服务器,都要和对方进行数据上的交互,这种交互也正是我们进程通信的主题。一个进程扮演客户端的角色,另外一个进程扮演服务器的角色,两个进程之间相互发送接收数据,这就是基于本地套接字的进程通信。发送和接收数据要使用write和read系统调用,它们的原形为:
int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);
其中socket为套接字描述符;len为需要发送或需要接收的数据长度;对于read系统调用,buffer是用来存放接收数据的缓冲区,即接收来的数据存入其中,是一个输出参数;对于write系统调用,buffer用来存放需要发送出去的数据,即buffer内的数据被发送出去,是一个输入参数;返回值为已经发送或接收的数据长度。例如客户端要发送一个“Hello”字符串给服务器,则代码如下:
char buffer[10] = "Hello";
write(client_sockfd, buffer, strlen(buffer));

交互完成后,需要将连接断开以节省资源,使用close系统调用,其原形为:
int close(int socket);
不多说了,直接使用,大家一定都会,呵呵!

上面所述的每个系统调用都有-1返回值,在调用不成功时,它们均会返回-1,这个特性可以使得我们用if-else或异常处理语句来处理错误,为我们提供了很大的方便。

SOCK_DGRAM数据报式本地套接字的应用场合很少,因为流式套接字在本地的连接时间可以忽略,所以效率并没有提高,而且发送接收都需要携带对方的本地地址,因此很少甚至几乎不使用,这里不再赘述,下篇网络套接字将会着重讨论。

与本地套接字相对应的是网络套接字,可以用于在网络上传送数据,换言之,可实现不同机器上的进程通信过程。在TCP/IP协议中,IP地址的首字节为127即代表本地,因此本地套接字通信可以使用IP地址为127.x.x.x的网络套接字来实现。因此,我们在这里就给出本地套接字通信的模板类了,等待下一个帖子给出网络套接字通信模板类的时候,大家只需要在构造函数中添上127开头的IP地址,就可以方便的完成本地通信了,呵呵,还是很神奇的嘛!

最后要和大家道个歉,由于本人最近的项目任务太多,这个帖子拖到现在才完成,可以说吊足了大家的胃口,嘿嘿,希望大家原谅,别拿柿子砸我啊!哈哈!好了,再见!