天天看點

UNIX網絡程式設計筆記(11)—UNIX域套接字UNIX網絡程式設計筆記(11)—UNIX域套接字

UNIX網絡程式設計筆記(11)—UNIX域套接字

1.簡介

Unix域協定并不是一個實際的協定族,而是在單個主機上執行客戶/伺服器通訊的一種方式,單個主機上執行通信,也就是所謂的進行間通信(IPC),是以Unix域套接字協定可以視作IPC方法之一。

Unix域提供兩類套接字:位元組流套接字(類似TCP)和資料報套接字(類似DUP)。

Unix域中用于辨別客戶和伺服器的協定位址是普通檔案系統中的路徑名(但需要和Unix域套接字關聯起來),否則無法讀寫這些檔案。回憶一蛤,IPv4的協定位址由一個32位位址和16位端口号構成,IPv6協定位址則由一個128位位址和16位端口号組成。

2.Unix域套接字位址結構

#include <sys/un.h>
struct sockaddr_un {
    sa_family_t sum_family;// AF_LOCAL或者AF_UNIX
    char sun_path[];//字元串指代路徑(null終止)
};
           

sun_path

表示與套接字關聯的位址,以NULL結尾,如果未指定位址通則通過以空字元串作為路徑名訓示,也就是說

sun_path[0]

值為0,這個效果就好像Ipv4的

INADDR_ANY

和IPv6的

ADDR_ANY_INIT

3.socketpair函數

這個是UNIX域套接字特有的函數,它建立兩個素侯連接配接起來的套接字。

#include <sys/socket.h>
int socketpair(int family , int type , int protocol ,int sockfd[]);
//傳回:成功則為0,出錯則為-1
           

3.1參數說明

family: 為

AF_LOCAL

或者

AF_UNIX

type:既可以是

SOCK_STREAM

也可以是

SOCK_DGRAM

protocol:必須為0。

sockfd[2]:新建立的兩個套接字别在sockfd[0]和sockfd[1]中傳回。

當設定type參數為

SOCK_STREAM

時,得到的結果就叫做流管道這與

pipe

建立的的普通UNIX管道類似了。差别在于流管道是全雙工的,也就是說,兩個描述符既可讀也可寫。回憶一蛤,用

pipe

建立的匿名管道,

pipefd[0]

用于讀操作,

pipefd[1]

用于寫操作。

4.UNIX域套接字程式設計

還是根據UNP1中的代碼寫的,主要工作就是把包裹函數拆開。

4.1 Unix域位元組流客戶/伺服器程式

跟TCP客戶服務程式類似,不過在

bind

步驟中,是将特定的

path

與套接字綁定。

代碼

伺服器程式

//unixdomainserv.c

#include <sys/un.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

#define UNIXDOMAIN_PATH "/tmp/unix.str"
#define MAXLEN 1024
void sig_child(int);
void str_echo(int );
typedef void(*sig_handle)(int);//handle

int main(int argc ,char **argv)
{
    int listenfd;
    int connfd;
    socklen_t clilen;
    struct sockaddr_un cliaddr,servaddr;
    pid_t childpid;

    if((listenfd = socket(AF_LOCAL,SOCK_STREAM,))<)//socket
    {
        printf("socket error\r\n");
        return -;
    }
    unlink(UNIXDOMAIN_PATH);//delete file

    bzero(&servaddr,sizeof(servaddr));
//    memset(&servaddr,0x00,sizeof(servaddr));

    servaddr.sun_family=AF_LOCAL;
    strcpy(servaddr.sun_path,UNIXDOMAIN_PATH);

    if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<)//bind
    {
        printf("bind error\r\n");
        return -;
    }

    if(listen(listenfd,)<)//listen
    {
        printf("listen error\r\n");
        return -;
    }

    sig_handle handle1 = sig_child;
    signal(SIGCHLD,handle1);

    while()
    {
        clilen = sizeof(cliaddr);
        if((connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen))<)
        {
            printf("accept error\r\n");
        }
        if((childpid= fork())==)//child process
        {
            close(listenfd);
            str_echo(connfd);
            return ;
        }
        close(connfd);//parent  close connfd
    }
    return ;
}

void str_echo(int connfd)
{
    ssize_t nread;
    char readbuff[MAXLEN];

    memset(readbuff,,sizeof(readbuff));
    while((nread=read(connfd,readbuff,MAXLEN))>)
    {
        write(connfd,readbuff,strlen(readbuff));

        memset(readbuff,,sizeof(readbuff));
    }

}



void sig_child(int signo)
{
    pid_t pid;
    int stat;
#if 1 
    while((pid=waitpid(-,&stat,WNOHANG))>)
    printf("waitpid:child terminated,pid=%d\r\n",pid);
#endif
    return ;
}
           

用戶端程式

#include <sys/un.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#define UNIXDOMAIN_PATH "/tmp/unix.str"
#define MAXLEN 1024


void str_cli(FILE*,int);

int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_un servaddr;
    if((sockfd = socket(AF_LOCAL,SOCK_STREAM,))<)//socket
    {
        printf("socket error\r\n");
        return -;
    }

    bzero(&servaddr,sizeof(servaddr));
//    memset(&servaddr,0x00,sizeof(servaddr));
    servaddr.sun_family=AF_LOCAL;//or AF_UNIX
    strcpy(servaddr.sun_path,UNIXDOMAIN_PATH);
    if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<)//connect
    {
        printf("conncet error\r\n");
        return -;
    }
    str_cli(stdin,sockfd);
    return ;
}

void str_cli(FILE*fp,int sockfd)
{
    int nread;
    int nwrite;
    char readbuff[MAXLEN];

    while( fgets(readbuff,sizeof(readbuff),fp)!=NULL)
    {
        if( (nwrite= write(sockfd,readbuff,strlen(readbuff)))<)
        {
            printf("write error \r\n");
            return ;
        }
        memset(readbuff,,sizeof(readbuff));
        if(( nread= read(sockfd,readbuff,sizeof(readbuff)))<)
        {
            printf("read error \r\n");
            return ;
        }
        fputs(readbuff,stdout);
    }
}
           

4.2 Unix域資料報客戶/伺服器程式

與UDP程式類似,但是用戶端需要有一些改變:

當使用Unix域資料報協定時候,我們必須顯示的bind一個路徑名到我們到套接字,這樣伺服器才會有能回射應答的路徑名。在書中用了

tmpnam

指派一個臨時路徑名,但是在編譯的時候說該函數已經不用了,取而代之的是

mkstemp

代碼

伺服器程式

#include <sys/un.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

#define UNIXDOMAIN_PATH "/tmp/unix.dg"
#define MAXLEN 1024
#define MAXLINE 1024
void dg_echo(int,struct sockaddr *,socklen_t );


int main(int argc ,char **argv)
{
    int sockfd;
    struct sockaddr_un cliaddr,servaddr;

    if((sockfd = socket(AF_LOCAL,SOCK_DGRAM,))<)//socket
    {
        printf("socket error\r\n");
        return -;
    }
    unlink(UNIXDOMAIN_PATH);//delete file

    bzero(&servaddr,sizeof(servaddr));
//    memset(&servaddr,0x00,sizeof(servaddr));

    servaddr.sun_family=AF_LOCAL;
    strcpy(servaddr.sun_path,UNIXDOMAIN_PATH);

    if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<)//bind
    {
        printf("bind error\r\n");
        return -;
    }
    dg_echo(sockfd,(struct sockaddr *)&cliaddr,sizeof(cliaddr));

    return ;
}
#if 1
void dg_echo(int sockfd ,struct sockaddr* pcliaddr,socklen_t clilen)
{
    char buf[MAXLEN];
    int n;
    socklen_t len ;
    while()
    {
        len = clilen;
        if((n=recvfrom(sockfd,buf,MAXLEN,,pcliaddr,&len))<)
        {
            printf("recvfrom error\r\n");
            return ;
        }
        sendto(sockfd,buf,n,,pcliaddr,len);
    }
}
#endif
           

用戶端程式

#include <sys/un.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>

#define UNIXDOMAIN_PATH "/tmp/unix.dg"
#define MAXLEN 1024


void dg_cli(FILE*,int,const struct sockaddr *,socklen_t);

int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_un servaddr;
    struct sockaddr_un cliaddr;

    if((sockfd = socket(AF_LOCAL,SOCK_DGRAM,))<)//socket
    {
        printf("socket error\r\n");
        return -;
    }

    bzero(&cliaddr,sizeof(cliaddr));
//    memset(&servaddr,0x00,sizeof(servaddr));
    cliaddr.sun_family=AF_LOCAL;
    char temp[]="template_XXXXXX";
    int fd = mkstemp(temp);
    strcpy(cliaddr.sun_path,temp);
    unlink(temp);
    close(fd);
    if(bind(sockfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr))<)//bind
    {
        printf("conncet error\r\n");
        return -;
    }
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sun_family=AF_LOCAL;
    strcpy(servaddr.sun_path,UNIXDOMAIN_PATH);
    dg_cli(stdin,sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
    return ;
}



void dg_cli(FILE*fp,int sockfd,const struct sockaddr*pservaddr,socklen_t servlen)
{
    int n;
    char sendbuff[MAXLEN];
    char recvbuff[MAXLEN+];
    while(fgets(sendbuff,MAXLEN,fp)!=NULL)
    {
        //指定伺服器套接字結構直接sendto
        sendto(sockfd,sendbuff,strlen(sendbuff),,pservaddr,servlen);
        if((n=recvfrom(sockfd,recvbuff,MAXLEN,,NULL,NULL))<=)
        {
            printf("recvfrom error\r\n");
            return ;
        }
        recvbuff[n]='\0';//防止越界
        fputs(recvbuff,stdout);//輸出回射資料
    }
}
           

4.3 附錄:Makefile

PROGS =unixdomainserv unixdomaincli unixdomainserv2 unixdomaincli2
CLEANFILES = core core.* *.core *.o temp.* *.out typescript* \
        *.lc *.lh *.bsdi *.sparc *.uw

all :${PROGS}
CFLAGS+=-g 

unixdomainserv:unixdomainserv.o
    ${CC} ${CFLAGS} -o [email protected] $^
unixdomaincli:unixdomaincli.o
    ${CC} ${CFLAGS} -o [email protected] $^
unixdomainserv2:unixdomainserv2.o
    ${CC} ${CFLAGS} -o [email protected] $^
unixdomaincli2:unixdomaincli2.o
    ${CC} ${CFLAGS} -o [email protected] $^
    @rm *o

clean:
    rm -f ${PROGS} ${CLEANFILES}
           

5.參考

1.《UNP1》