进程笔记2:进程之间的通信(UNIX域套接字socket)

时间:2022-06-26 15:24:11

socket的地址数据结构根据不同的系统以及网络环境有不同形式。为了使不同格式地址能够被传入套接字函数,必须强制将地址结构转换为:

struct sockaddr{
   sa_family_t sa_family; /* address family*/
   char        sa_data[]; /* variable-length address*/
   ...
};

套接字实现可以*地添加额外的成员并且定义sa_data成员的大小。例如在linux中,该结构定义如下

struct sockaddr{
   sa_family_t sa_family;   /* address family*/
   char        sa_data[14]; /* variable-length address*/
};

其中sa_family_t表示套接字的通信域。主要有以下四个值


描述
AF_INET IPv4因特网域
AF_INET6 IPv6因特网域
AF_UNIX UNIX域
AF_UNSPEC 未指定


创建套接字的函数如下

#include <sys/socket.h>
int socket(int domain, int type, int protocol);/*成功返回文件(套接字)描述符,出错返回-1

其中domain指代通信域,type指代套接字类型,主要有以下四种

类型 描述
SOCK_DGRAM 长度固定的、无连接的不可靠报文传递
SOCK_RAM IP协议的数据报接口
SOCK_SEQPACKET 长度固定、有序、可靠的面向连接报文传递
SOCK_STREAM 有序、可靠、双向的面向连接字节流

参数protocol通常是零,表示按给定的域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用protocol参数选择一个特定协议。

一个多进程间利用UNIX域套接字(只用在本地)进行通信的例子( 代码分两部分不方便观察,后面socket_unix.c将其合为了一部分

 /*
domain_socket.h
@Author: duanjigang @2006-4-11
@Desp: declaratin of methods used for unix-domain-socket communication 
*/
#ifndef _H_
#define _H_
#include <stdio.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>
#define MSG_SIZE 1024
int init_send_socket(struct sockaddr_un * addr,char * path)
{
        int sockfd,len;
        sockfd=socket(AF_UNIX,SOCK_DGRAM,0);
        if(sockfd<0)
        {
                exit(1);
        }
        bzero(addr,sizeof(struct sockaddr_un));
        addr->sun_family=AF_UNIX;
        strcpy(addr->sun_path,path);
        return sockfd;
}
int init_recv_socket(char * path)
{
        int sockfd,len; 
        struct sockaddr_un addr; 
        sockfd=socket(AF_UNIX,SOCK_DGRAM,0); 
        if(sockfd<0) 
        { 
           return -1;
        } 
        bzero(&addr,sizeof(struct sockaddr_un)); 
        addr.sun_family = AF_UNIX; 
        strcpy(addr.sun_path, path);
        unlink(path);
        len = strlen(addr.sun_path) + sizeof(addr.sun_family);
        if(bind(sockfd,(struct sockaddr *)&addr,len)<0) 
         { 
          return -1;
         } 
        return sockfd;

}
int receive_from_socket(int sockfd, char msg[])
{
      int n; 
      memset(msg, 0, MSG_SIZE);
      n=recvfrom(sockfd, msg, MSG_SIZE, 0, NULL, NULL); 
      if(n<=0)
      {
       return -1;
      }
      msg[n]=0;
      return n;
}
int send_to_socket(int sockfd, char msg[], const struct sockaddr_un * addr)
{
        int len; 
        len = strlen(addr->sun_path)+sizeof(addr->sun_family);
        sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)addr,len); 
        return 1;
}
#endif

/*
main.c
@Author: duanjigang @ 2006-4-11
@Desp: Two processes communicate with unix domain socket
*/
#include "domain_socket.h"
#define PATH "/home/useless"
/*
进程间通过域进行通讯-举例:父子进程,一个发送,一个接收
*/
int main(void)
{
  int pid;
  /*
  子进程用于发送消息
  */
  if((pid = fork()) == 0)
  {
    int fd, counter = 0;
    char send_buffer[MSG_SIZE];
    struct sockaddr_un addr;
    if( (fd = init_send_socket(&addr, PATH)) > 0)
    while(1)
    {
       memset(send_buffer, 0 , MSG_SIZE);
       /*
           防止计数器越界,所以做一个复位判断
           */
       sprintf(send_buffer,"message for %d times",counter++ >= 10000 ? 1 : counter);
       send_to_socket(fd, send_buffer, &addr);
       printf("Sender: %s\n", send_buffer);
       sleep(1);
    }
  }/*
   父进程用于接收消息
  */
  else
  {
      int fd;
      char recv_buffer[MSG_SIZE];
      if( (fd = init_recv_socket(PATH))> 0)
      while(1)
      {
       memset(recv_buffer, 0, MSG_SIZE);
       if(receive_from_socket(fd, recv_buffer))
       {
            printf("Receiver: %s\n", recv_buffer);
       }
      }
  }
}
 

运行结果示例:

Sender: message for 1 times

Sender: message for 2 times

Receiver: message for 2 times

Sender: message for 3 times

Receiver: message for 3 times

Sender: message for 4 times

Receiver: message for 4 times

Sender: message for 5 times

Receiver: message for 5 times


socket_unix.c

//利用UNIX域套接字通信

#include <stdio.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

#define MSG_MAX_SIZE 1024
#define PATH "a.socket"//套接字文件

int main()
{
    int len;
    int socket_fd;
    struct sockaddr_un addr;

    bzero(&addr, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, PATH); 
    len = strlen(addr.sun_path)+sizeof(addr.sun_family);
    
    if(!fork())//子进程内部代码
    {
        int counter = 0;
        char send_buffer[MSG_MAX_SIZE];

        //init send socket
        socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); 
        if(socket_fd<0)
        {
            printf("client socket error!");
            return 0;
        }
        
        while(1) //循环发送数据消息
        {
            memset(send_buffer, 0, MSG_MAX_SIZE);
            sprintf(send_buffer, "message for %d times", counter++);
            
            //将数据信息发送到addr指定的套接字文件之中,所以这样进程之间就可以进行通信
            sendto(socket_fd, send_buffer, strlen(send_buffer), 0, (struct sockaddr*)&addr, len);

            printf("sender:%s\n", send_buffer);
            sleep(1);
        }
    }
    else
    {
        char recv_buffer[MSG_MAX_SIZE];

        //init recv socket
        socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
        if(socket_fd<0)
        {
            printf("server socket error!");
            return 0;
        }
        
        unlink(PATH);//防止要创建的socket文件已存在
        if(bind(socket_fd, (struct sockaddr *)&addr, len)<0)//只有bind以后才会在硬盘创建套接字PATH
        {
            printf("bind error");
            return 0;
        }

        while(1)//循环接收数据
        {
            memset(recv_buffer, 0, MSG_MAX_SIZE);

            //receive from socket从指定socket中读取数据,这里socket已经绑定了指定的PATH的文件
            recvfrom(socket_fd, recv_buffer, MSG_MAX_SIZE, 0, NULL, NULL);
            
            printf("receive Message: %s\n", recv_buffer);
        }
    }
    
    return 0;
}