其主要流程如下:
客戶端:
//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);