网络编程进阶--非网络通信套接字

1.非命名UNIX域套接字

linux下环境下使用socketpair函数创造一对未命名的,相互连接的UNIX域套接字:

#include <sys/socket.h>

int socketpair(int domain,int type,int protocol,int sockfd[]);

 

第一个参数用于表示创建的套接字的域,本地进程通信时,用AF_UNIX。

第二个参数表示要创建的套接字的类型,因为本地连接时可靠地,所以指定为SOCK_STREAM。

第三个参数表示所用的协议,通常设为0.

第四个参数表示存储套接字描述符的数组。

 

非命名UNIX域套接字类似于非命名管道,只有保存了域套接字文件描述符的进程可以使用它。

 

//s_pipe.c

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/socket.h>

 

#define MAX 100

int main()

{

int sockfd[2];

pid_t pid;

char buf[MAX];

int n;

 

if(socketpair(AF_UNIX,SOCK_STREAM,0,sockfd)==-1){

perror("fail to create socker");

exit(1);

}else if(pid==0){

close(sockfd[0]);

strcpy(buf,"hello parent");

n=strlen(buf);

 

if(write(sockfd[1],buf,n)==-1){

perror("fall to write");

exit(1);
}

printf("the chlld done/n");

}else{

close(sockfd[1]);

if(read(sock[0],buf,MAX)==-1){

perror("fail to read");

exit(1);

}

 

n=strlen(buf);

buf[n]='/0';

 

printf("receive from child :%s/n",buf);

 

if(wait(NULL)==NULL){

perror("fail to wait");

exit(1);

}

 

printf(":the parent done/n");

}

 

return 0 ;

}

 

 

2.命名UNIX域套接字

与网络痛惜套接字一样,UNIX域套接字也要绑定地址,不过由于两者所是哟个的域不一样,所以其地址族也不一样。unix域套接字使用sockaddr_un结构表示,定义如下:

#include <sys/un.h>

struct sockaddr_un{

sa_family_t sun_family;//通信域,即AF_UNIX

char sun_path[108];//套接字文件的路径名

}

 

第二个参数是一个路径名,该地址绑定到一个UNIX域套接字时系统会创建一个文件,其路径名为sun_path中所表示的路径名。

在调用bind函数进行地址绑定时,需要将字符数组sun_path的大小作为参数传入,如下:

 

int sfd;

struct sockaddr_un un;

 

bind(sfd,(struct sockaddr *)&un,sizeof(struct sockaddr_un)-sizeof(sa_family_t));

 

如果绑定时,sun_path所指定的文件存在,bind会失败。每次应用程序结束时,该文件并不会被系统自动删除,因此应该在程序结束时,调用unlink函数手动删除。

通常的做法是使用进程的ID作为文件名,这样可以保证文件名不冲突。

 

 

3.UNIX域套接字实例--服务器端

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <stddef.h>
#include <time.h>

/* 使用/home/admin/connect.socket这个文件作为通信的套接字文件 */
#define PATH "/home/admin/connect.socket"
/* 如果超过这个时间,服务器进程将不处理客户端进程的连接请求 */
#define STALE 30
#define MAX 1024

/* 处理函数,用于将大写字符转换为小写字符。参数为需要转换的字符串 */
void my_fun(char * p)
{
    if(p == NULL) /* 空串 */
        return;

    for (; *p != '/0'; p++)
        if(*p >= 'A'&& *p <= 'Z') /* 判断字符并进行转换,也可以使用库函数 */
            *p = *p -'A'+ 'a';
}

/* 初始化函数,创建套接字,并且将该套接字绑定到一个地址(指定的文件)上
*  成功返回0,失败返回-1
*  lfd : 指向保存创建好的套接字的描述符的区域
*  path : 指定文件的路径,该文件必须是不存在的,否则bind函数会出错
*/
int init(int *lfd, char *path)
{
        int fd, len;
        struct sockaddr_un un_addr;

        /* 创建一个套接字,使用Unix域,类型为数据流类型 */
        if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1){
        perror("fail to create socket");
           return -1;
}
   
    /* 如果套接字文件已经存在则删除,否则unlink函数出错,因此不检查unlink函数的返回值 */
unlink(path);

        /* 设置地址结构,该地址不再由IP地址和进程号组成,而是一个文件路径 */
        memset(&un_addr, 0, sizeof(un_addr));
        un_addr.sun_family = AF_UNIX; /* 使用Unix域地址族 */
        strcpy(un_addr.sun_path, path); /* 复制套接字文件的路径 */

        len = offsetof(struct sockaddr_un, sun_path) + strlen(path); /* 计算文件路径的长度 */

    /* 将套接字绑定到一个地址结构上,该套接字可以开始通信 */
        if (bind(fd, (struct sockaddr *)&un_addr, len) == -1){
            perror("fail to bind");
goto err;
        }

    /* 开始监听,最大连接数为10 */
        if (listen(fd, 10) == -1){
            perror("fail to listen");
goto err;
        }

    *lfd = fd; /* 保存套接字的文件描述符 */
    return 0; /* 执行到这里,正常返回 */

err:
        close(fd); /* 出错,关闭套接字 */
return -1; /* 返回-1 */
}

int main(void)
{
int lfd, cfd;
time_t staletime; /* 测试客户端进程的时间 */
        struct sockaddr_un un_addr;
struct stat statbuf;
char buf[MAX];
    int len, n;

if(init(&lfd, PATH) == -1) /* 调用初始化函数,创建监听套接字,并且开始监听 */
    exit(1);
   
while(1){/* 服务器程序多半是死循环 */
        len = sizeof(struct sockaddr_un);
       
        /* 处理一个连接,创建连接套接字,得到客户端进程的套接字文件路径
* 并将其保存在un_addr结构中,注意这里使用sockaddr_un结构的大小
*/
            if ((cfd = accept(lfd, (struct sockaddr *)&un_addr, &len)) == -1){
            perror("fail to accept");
                exit(1);    
        }

            /* 得到客户端文件的路径,并且设置结束符 */
            len -= offsetof(struct sockaddr_un, sun_path);
            un_addr.sun_path[len] = '/0';

        /* 得到文件爱你的状态信息,为了验证客户端进程的通信时间
* 客户端进程如果长期没有修改通信用的套接字文件,说明该客户端有可能已经结束通信
* 下面分别验证文件的权限和修改时间,这些操作并不是必须的
* 但是出于程序的完整性考虑,这些操作还是必要的
*/
            if (stat(un_addr.sun_path, &statbuf) == -1) {
                perror("fail to get status");
            exit(1);
            }

        /* 检查文件的权限,通信用的套接字文件的权限必须是"rwx------"
* 也就是说之用所有者用户可以有读、写和执行该文件的权限,其他用户没有
* 这说明Unix域套接字只能用于同一用户的进程之间的通信
*/
            if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) || (statbuf.st_mode & S_IRWXU) != S_IRWXU) {
              printf("wrong permissions/n");
                 exit(1);
            }
       
        /* 检查套接字文件的更新时间,超过三十秒钟未作访问和修改
* 说明客户端进程可能已经断开了连接,关闭连接套接字,结束连接
*/
            staletime = time(NULL) - STALE;
            if (statbuf.st_atime < staletime || statbuf.st_ctime < staletime || statbuf.st_mtime < staletime){
              printf("client is too old/n");
              close(cfd);
            break;
            }

        /* 删除客户端的套接字文件
* 该套接字文件由客户端进程在调用bind函数进行套接字绑定的时候生成
*/
            if(unlink(un_addr.sun_path) == -1){
            perror("fail to unlink");
            exit(1);
        }
        
        my_fun(buf); /* 调用大小写转换函数 */
if(write(cfd, buf, n) == -1){ /* 将转换后的字串发给客户端进程 */
              perror("fail to write");
            exit(1);
        }
       
        close(cfd); /* 通讯结束,关闭套接字,准备下一次通信 */
    }

    /* 删除服务器进程的套接字文件 */
    if(unlink(PATH) == -1){
        perror("fail to unlink");
        exit(1);
    }
    close(lfd);
    return 0;
}

 

 

4.UINX域套接字实例--客户端

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>

/* 使用/home/admin/connect.socket这个文件作为通信的套接字文件 */
#define PATH "/home/admin/connect.socket"
/* 客户端的套接字文件路径的目录,文件名为进程的进程ID */
#define C_PATH "/home/admin/"
#define MAX 1024

int main(void)
{
    int    cfd, len;
    struct sockaddr_un un_addr;
    char buf[MAX];

        if ((cfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1){ /* 创建一个连接 */
        perror("fail to create socket");
        exit(1);
    }

        /* 设置客户端进程使用的套接字的路径名和套接字的域 */
        memset(&un_addr, 0, sizeof(struct sockaddr_un));
        un_addr.sun_family = AF_UNIX;
    /* 客户端的套接字文件名为默认目录 + 进程ID */
        sprintf(un_addr.sun_path, "%s%d", C_PATH, getpid());
        len = offsetof(struct sockaddr_un, sun_path) + strlen(un_addr.sun_path);

    /* 如果套接字文件已经存在则删除,否则unlink函数出错,因此不检查unlink函数的返回值 */
        unlink(un_addr.sun_path);

    /* 绑定客户端套接字文件,该文件由服务器端进程负责删除 */
    if (bind(cfd, (struct sockaddr *)&un_addr, len) == -1){
            perror("fail to bind");
        exit(1);
        }
   
    /* 改变套接字文件的权限为rwx------ */
        if (chmod(un_addr.sun_path, S_IRWXU) < 0) {
             perror("fail to change model");
        exit(1);
        }
  
        memset(&un_addr, 0, sizeof(struct sockaddr_un));
        un_addr.sun_family = AF_UNIX;
        strcpy(un_addr.sun_path, PATH); /* 服务器套接字文件的路径 */

        len = offsetof(struct sockaddr_un, sun_path) + strlen(buf);

    /* 使用服务器的套接字文件进行连接 */
        if(connect(cfd, (struct sockaddr *)&un_addr, len) < 0) {
            perror("fail to connect");
        exit(1);
}

strcpy(buf, "CHina");
if(write(cfd, buf, strlen(buf) + 1) == -1){ /* 发送字符串,该串包含‘/0’结束符 */
perror("fail to write");
        exit(1);
    }

/* 读取服务器程序发回的串*/
if(read(cfd, buf, MAX) == -1){
        perror("fail to read");
exit(1);
}
    
printf("recive from server: %s/n", buf); /* 打印该串 */
     close(cfd);

    return 0;
}

你可能感兴趣的:(网络编程进阶--非网络通信套接字)