16.unix域協議

程式設計-浪子發表於2015-05-17

一.Unix域協議

Uinx域套接字有幾個理由:

         UNIX域套接字用於在同一臺機器上執行的程式之間的通訊。雖然因特網域套接字也可以達到同一目的,但UNIX域套接字的效率更高

         UNIX域套接字僅僅複製資料;它們並不執行協議處理,不需要新增或刪除網路報頭,無需計算檢驗和,不要產生順序號,無需傳送確認報文。

         UNIX域套接字提供流和資料包兩種介面。

         UNIX域資料包服務是可靠的,既不會丟失訊息也不會傳遞出錯。

         UNIX域套接字是套接字和管道之間的混合物。為了建立一對非命名的、相互連線的UNIX域套接字,使用者可以使用它們面向網路的域套接字介面,也可使用socketpair函式

         UNIX域套接字可用在同一個主機上的不同程式之間傳遞描述符.

在<sys/un.h>中有定義Unix域套接字地址結構,在ubuntu中的路徑為:/usr/include/linux/un.h

struct sockaddr_un {
    __kernel_sa_family_t   sun_family; /* AF_UNIX */
    char   sun_path[UNIX_PATH_MAX];    /* pathname */
};

二.利用Unix域協議實現回射C/S伺服器

echoser.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#include <sys/un.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>


#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)
     
void echo_ser(int conn)
{
	char recvbuf[1024];
	int n;
	while(1)
	{
		bzero(recvbuf,sizeof(recvbuf));
		n = read(conn,recvbuf,sizeof(recvbuf));
		if(n==-1)
		{
			if(n == EINTR) // 由於訊號中斷,沒讀到任何資料。
				continue;
			ERR_EXIT("read err");
		}
		else if(n == 0)
		{
			printf("client close\n");
			break;
		}
		fputs(recvbuf,stdout);
		write(conn,recvbuf,strlen(recvbuf));
	}
	close(conn);
} 

     
int main()
{
	int listenfd;
	if( (listenfd = socket(AF_UNIX,SOCK_STREAM,0)) < 0)
		ERR_EXIT("socket err");
		
	unlink("/tmp/test_socket"); // 刪除檔案
	
	struct sockaddr_un servaddr;
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path,"/tmp/test_socket");
	
	if( bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
		ERR_EXIT("bind err");
	if( listen(listenfd,SOMAXCONN) < 0)
		ERR_EXIT("listen err");
	
	int conn;
	pid_t pid;
	while(1)
	{
		conn = accept(listenfd,NULL,NULL);
		if(conn == -1)
		{
			if(conn == EINTR)
				continue;
			ERR_EXIT("accept err");
		}		
		pid = fork();
		if(pid == -1)
			ERR_EXIT("fork err");
		if(pid == 0) // 子程式,不需要處理監聽
		{
			close(listenfd);
			echo_ser(conn);
			exit(EXIT_SUCCESS);
		}
		// 父程式
		close(conn); // 父程式不需要已連線套接字
	}
}




echocli.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#include <sys/un.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>



#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)
        
void echo_cli(int sock)
{
	char sendbuf[1024] = {0};
	char recvbuf[1024] = {0};
	
	while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
	{
		// 傳送資料
		write(sock,sendbuf,strlen(sendbuf));
		//接收資料
		read(sock,recvbuf,sizeof(recvbuf));
		// 列印接收緩衝區的資料
		fputs(recvbuf,stdout);
		// 緩衝區清空
		bzero(sendbuf,sizeof(sendbuf));
		bzero(recvbuf,sizeof(recvbuf));
		 
	}
}        
        
int main()
{
	int sock;
	if ((sock = socket(AF_UNIX,SOCK_STREAM,0))<0)
		ERR_EXIT("socket err");
	
	struct sockaddr_un servaddr;
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path,"/tmp/test_socket");
	
	if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
		ERR_EXIT("connect err"); 
		
	echo_cli(sock);
	return 0;
}

三.Unix域協議程式設計要點

        (1)bind 成功時將建立一個檔案,許可權為0777 & ~umask
        (2)sun_path 最好用一個絕對路徑
        (3)UNIX域協議支援流式套接字與報文套接字
        (4)UNIX域流式套接字connect發現監聽佇列滿時立刻返回一個ECONNREFUSED,這和TPC不同,如果監聽佇列滿,會忽略到來的SYN,這導致對方重傳SYN


相關文章