socket的地址数据结构根据不同的系统以及网络环境有不同形式。为了使不同格式地址能够被传入套接字函数,必须强制将地址结构转换为:
struct sockaddr{ sa_family_t sa_family; /* address family*/ char sa_data[]; /* variable-length address*/ ... };
套接字实现可以*地添加额外的成员并且定义sa_data成员的大小。例如在linux中,该结构定义如下
struct sockaddr{ sa_family_t sa_family; /* address family*/ char sa_data[14]; /* variable-length address*/ };
其中sa_family_t表示套接字的通信域。主要有以下四个值
域 | 描述 |
AF_INET | IPv4因特网域 |
AF_INET6 | IPv6因特网域 |
AF_UNIX | UNIX域 |
AF_UNSPEC | 未指定 |
创建套接字的函数如下
#include <sys/socket.h> int socket(int domain, int type, int protocol);/*成功返回文件(套接字)描述符,出错返回-1
其中domain指代通信域,type指代套接字类型,主要有以下四种
类型 | 描述 |
SOCK_DGRAM | 长度固定的、无连接的不可靠报文传递 |
SOCK_RAM | IP协议的数据报接口 |
SOCK_SEQPACKET | 长度固定、有序、可靠的面向连接报文传递 |
SOCK_STREAM | 有序、可靠、双向的面向连接字节流 |
参数protocol通常是零,表示按给定的域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用protocol参数选择一个特定协议。
一个多进程间利用UNIX域套接字(只用在本地)进行通信的例子( 代码分两部分不方便观察,后面socket_unix.c将其合为了一部分)
/* domain_socket.h @Author: duanjigang @2006-4-11 @Desp: declaratin of methods used for unix-domain-socket communication */ #ifndef _H_ #define _H_ #include <stdio.h> #include <unistd.h> #include <sys/un.h> #include <sys/socket.h> #define MSG_SIZE 1024 int init_send_socket(struct sockaddr_un * addr,char * path) { int sockfd,len; sockfd=socket(AF_UNIX,SOCK_DGRAM,0); if(sockfd<0) { exit(1); } bzero(addr,sizeof(struct sockaddr_un)); addr->sun_family=AF_UNIX; strcpy(addr->sun_path,path); return sockfd; } int init_recv_socket(char * path) { int sockfd,len; struct sockaddr_un addr; sockfd=socket(AF_UNIX,SOCK_DGRAM,0); if(sockfd<0) { return -1; } bzero(&addr,sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, path); unlink(path); len = strlen(addr.sun_path) + sizeof(addr.sun_family); if(bind(sockfd,(struct sockaddr *)&addr,len)<0) { return -1; } return sockfd; } int receive_from_socket(int sockfd, char msg[]) { int n; memset(msg, 0, MSG_SIZE); n=recvfrom(sockfd, msg, MSG_SIZE, 0, NULL, NULL); if(n<=0) { return -1; } msg[n]=0; return n; } int send_to_socket(int sockfd, char msg[], const struct sockaddr_un * addr) { int len; len = strlen(addr->sun_path)+sizeof(addr->sun_family); sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)addr,len); return 1; } #endif
/* main.c @Author: duanjigang @ 2006-4-11 @Desp: Two processes communicate with unix domain socket */ #include "domain_socket.h" #define PATH "/home/useless" /* 进程间通过域进行通讯-举例:父子进程,一个发送,一个接收 */ int main(void) { int pid; /* 子进程用于发送消息 */ if((pid = fork()) == 0) { int fd, counter = 0; char send_buffer[MSG_SIZE]; struct sockaddr_un addr; if( (fd = init_send_socket(&addr, PATH)) > 0) while(1) { memset(send_buffer, 0 , MSG_SIZE); /* 防止计数器越界,所以做一个复位判断 */ sprintf(send_buffer,"message for %d times",counter++ >= 10000 ? 1 : counter); send_to_socket(fd, send_buffer, &addr); printf("Sender: %s\n", send_buffer); sleep(1); } }/* 父进程用于接收消息 */ else { int fd; char recv_buffer[MSG_SIZE]; if( (fd = init_recv_socket(PATH))> 0) while(1) { memset(recv_buffer, 0, MSG_SIZE); if(receive_from_socket(fd, recv_buffer)) { printf("Receiver: %s\n", recv_buffer); } } } }
运行结果示例:
Sender: message for 1 times Sender: message for 2 times Receiver: message for 2 times Sender: message for 3 times Receiver: message for 3 times Sender: message for 4 times Receiver: message for 4 times Sender: message for 5 times Receiver: message for 5 times
socket_unix.c
//利用UNIX域套接字通信 #include <stdio.h> #include <unistd.h> #include <sys/un.h> #include <sys/socket.h> #define MSG_MAX_SIZE 1024 #define PATH "a.socket"//套接字文件 int main() { int len; int socket_fd; struct sockaddr_un addr; bzero(&addr, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, PATH); len = strlen(addr.sun_path)+sizeof(addr.sun_family); if(!fork())//子进程内部代码 { int counter = 0; char send_buffer[MSG_MAX_SIZE]; //init send socket socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); if(socket_fd<0) { printf("client socket error!"); return 0; } while(1) //循环发送数据消息 { memset(send_buffer, 0, MSG_MAX_SIZE); sprintf(send_buffer, "message for %d times", counter++); //将数据信息发送到addr指定的套接字文件之中,所以这样进程之间就可以进行通信 sendto(socket_fd, send_buffer, strlen(send_buffer), 0, (struct sockaddr*)&addr, len); printf("sender:%s\n", send_buffer); sleep(1); } } else { char recv_buffer[MSG_MAX_SIZE]; //init recv socket socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); if(socket_fd<0) { printf("server socket error!"); return 0; } unlink(PATH);//防止要创建的socket文件已存在 if(bind(socket_fd, (struct sockaddr *)&addr, len)<0)//只有bind以后才会在硬盘创建套接字PATH { printf("bind error"); return 0; } while(1)//循环接收数据 { memset(recv_buffer, 0, MSG_MAX_SIZE); //receive from socket从指定socket中读取数据,这里socket已经绑定了指定的PATH的文件 recvfrom(socket_fd, recv_buffer, MSG_MAX_SIZE, 0, NULL, NULL); printf("receive Message: %s\n", recv_buffer); } } return 0; }