linux 系統下使用socket進行本地程序間通訊
其主要流程如下:
客戶端:
//client #include<stdio.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<sys/un.h> #define UNIX_DOMAIN "/home/zhangmiaoling/test/socket/UNIX.domain" int main(){ int connect_fd; int ret; char send_buff[1024]; int i; static struct sockaddr_un srv_addr; // creat unix socket connect_fd=socket(PF_UNIX,SOCK_STREAM,0); if(connect_fd<0){ perror("cannot creat socket"); return -1; } srv_addr.sun_family=AF_UNIX; strcpy(srv_addr.sun_path,UNIX_DOMAIN); //connect server ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); if (ret<0){ perror("cannot connect server"); close(connect_fd); return -1; } memset(send_buff,0,1024); strcpy(send_buff,"message from client"); //send info server write(connect_fd,send_buff,sizeof(send_buff)); close(connect_fd); return 0; }
伺服器端:
//server #include<stdio.h> #include<sys/socket.h> #include<sys/types.h> #include<sys/un.h> #define UNIX_DOMAIN "/home/zhangmiaoling/test/socket/UNIX.domain" int main(){ socklen_t clt_addr_len; int listen_fd; int com_fd; int ret; int i; static char rcv_buff[1024]; int len; struct sockaddr_un clt_addr; struct sockaddr_un srv_addr; listen_fd=socket(AF_UNIX,SOCK_STREAM,0); if(listen_fd<0){ perror("connect creat communication socket"); } // set srv_addr param srv_addr.sun_family=AF_UNIX; strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1); unlink(UNIX_DOMAIN); //bind sockfd&addr ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); if(ret<0){ perror("cannot bind server socket"); close(listen_fd); unlink(UNIX_DOMAIN); return -1; } //listen sockfd ret=listen(listen_fd,1); if(ret<0){ perror("cannot listen sockfd"); close(listen_fd); unlink(UNIX_DOMAIN); return -1; } //have connect requst use accept len=sizeof(clt_addr); com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len); if(com_fd<0){ perror("cannot accept requst"); close(listen_fd); unlink(UNIX_DOMAIN); return -1; } //read and printf client send info printf("\n******info********\n"); //for(i=0,i<4,i++){ for(i=0;i<4;i++){ memset(rcv_buff,0,1024); int num = read(com_fd,rcv_buff,sizeof(rcv_buff)); printf("message from client %d : %s\n",num,rcv_buff); } close(com_fd); close(listen_fd); unlink(UNIX_DOMAIN); return 0; }
一. 建立socket
建立socket,型別為AF_LOCAL或AF_UNIX,表示用於程序通訊:
呼叫函式socket(),其原型如下:
int socket(int domain, int type, int protocol);
引數:
domain:指定協議族,對於本地套接字來說,值必須設定為AF_UNIX列舉值;
type:指定套接字型別,可以被設定為SOCK_STREAM(流式套接字)活SOCK_DGRAM(資料報式套接字)
protocol:指定具體的協議,應被設定為0
返回值為生成的套接字描述符。
對於本地套接字來說,流式套接字(SOCK_STREAM)是一個有順序的、可靠的雙向位元組流,相當於在本地程序之間建立起一條資料通道;資料報式套接字(SOCK_DGRAM)相當於單純的傳送訊息,在程序通訊過程中,理論上可能會有資訊丟失、複製或者不按先後次序到達的情況,但由於其在本地通訊,不通過外界網路,這些情況出現的概率很小。
二. 設定socket引數
SOCK_STREAM式本地套接字的通訊雙方均需要有本地地址,其中伺服器端的本地地址需要明確指定,指定方法是使用struct sockaddr_un型別的變數
struct sockaddr_un{
sa_family_t sun_family; // AF_UNIX
char sun_path[UNIX_PATH_MAX]; // 路徑名
}
三. 繫結
繫結要使用 bind 系統呼叫,其原形如下:
int bind(int socket, const struct sockaddr *address, size_t address_len);
引數
socket:服務端套接字描述符
address:需要繫結的服務端本地地址
address_len:本地地址的位元組長度
四. 監聽
伺服器端套接字建立完畢並賦予本地地址值(名稱,本例中為CAN_SERVICE)後,需要進行監聽,等待客戶端連線並處理請求,監聽使用 listen 系統呼叫,接受客戶端連線使用accept系統呼叫,它們的原形如下:
int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);
引數
socket:表示伺服器端的套接字描述符;
backlog 表示排隊連線佇列的長度(若有多個客戶端同時連線,則需要進行排隊);
address 表示當前連線客戶端的本地地址,該引數為輸出引數,是客戶端傳遞過來的關於自身的資訊;
address_len 表示當前連線客戶端本地地址的位元組長度,這個引數既是輸入引數,又是輸出引數。實現監聽、接受和處理。
五. 連線
客戶端需要socket系統呼叫connect()連線到服務端,其函式原型如下:
int connect(int socket, const struct sockaddr *address, size_t address_len);
引數
socket:客戶端的套接字描述符
address:當前客戶端的本地地址,是一個 struct sockaddr_un 型別的變數
address_len:表示本地地址的位元組長度
五. 資料互動
無論客戶端還是伺服器,都要和對方進行資料上的互動。一個程序扮演客戶端的角色,另外一個程序扮演伺服器的角色,兩個程序之間相互發送接收資料,這就是基於本地套接字的程序通訊。
迴圈讀取客戶端傳送的訊息,當客戶端沒有傳送資料時會阻塞直到有資料到來。如果想要多個連線併發處理,需要建立執行緒,將每個連線交給相應的執行緒併發處理。接收到資料後,進行相應的處理,將結果返回給客戶端。傳送和接收資料要使用 write 和 read 系統呼叫,它們的原形為:
int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);