本文章主要讲解linux网络编程socket通讯中常用到的结构体组成和作用
下图为各个结构间的对应关系(图来源于“爱编程的大丙”)
1.结构体sockaddr:
/************************************
**sa_family : 地址协族议,常用值:AF_INET(网络通讯) / AF_UNIX(linux 本地通讯)
**sa_data : 该数组存储网络端口port,IP地址(网络字节序),多出内存保留
*************************************/
struct sockaddr
{
unsigned short sa_family; /* address family, AF_xxx:地址族协议 */
char sa_data[14]; /* 14 bytes of protocol address:// 端口(2字节) + IP地址(4字节) + 填充(8字节)*/
};
该结构体 struct sockaddr一般用于以下socket通讯api函数
int bind(int sockfd, const struct sockaddr * my_addr, socklen_t addrlen);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int connect(int soctfd, const struct sockaddr * addr, int addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
用户一般不会直接使用结构体struct sockaddr,通常使用结构体 struct sockaddr_in或者 struct sockaddr_un强制转换成struct sockaddr 类型使用;例如下面函数使用:
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
servaddr.sin_port = htons(8888);
if(connect(socketfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0 )
{
perror("socket connect failed!");
return -1;
}
2.结构体 struct sockaddr_in
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
typedef unsigned short int sa_family_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
struct in_addr
{
in_addr_t s_addr;
};
// sizeof(struct sockaddr) == sizeof(struct sockaddr_in)
struct sockaddr_in
{
sa_family_t sin_family; /* 地址族协议: AF_INET*/
in_port_t sin_port; /* 端口, 2字节-> 大端(网络字节序) */
struct in_addr sin_addr; /* IP地址, 4字节 -> 大端(网络字节序) */
unsigned char sin_zero[8]; /* 填充 8字节 */
};
结构体 struct sockaddr_in适用于socket 网络套接字通讯
3.结构体struct sockaddr_un
typedef unsigned short int sa_family_t;
struct sockaddr_un
{
sa_family_t sun_family; /*地址族协议:PF_UNIX或AF_UNIX */
char sun_path[108]; /* 路径名 */
};
结构体struct sockaddr_un适用于本地进程间通讯的UNIX套接字通讯。进程间通信的一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式。这样做可以避免为黑客留下后门。使用UNIX套接字跟使用网络套接字类似,只需将变量参数 struct sockaddr_in 换成struct sockaddr_un输入到响应的socket套接字api函数上即可,如下所示:
/****************************** socket 网络套接字通讯 ******************************/
struct sockaddr_in server;
int listenfd
socklen_t len;
if( (socketfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1) //socket函数类似于open函数,打开并创建一个socket,AF_INET使用IPV4协议族,SOCK_STREAM为TCP套接口
{
perror("socket create failed!");
return -1;
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET; //IPV4协议
server.sin_port = htons(8888); //端口号
server.sin_addr.s_addr = htonl(INADDR_ANY); //自动生成自身IP做服务器IP给客户端连接
len = sizeof(struct sockaddr);
if(bind(listenfd, (struct sockaddr *)&server, len)<0) //绑定
{
perror("bind error.");
return -1;
}
........ /*省略后续socket api函数配置*/
/****************************** socket UNIX套接字通讯 ******************************/
struct sockaddr_un server;
int listenfd;
if( (listenfd = socket(AF_UNIX, SOCK_STREAM, 0) ) == -1) //进程间socket UNIX套接字通讯
{
perror("socket create failed!");
return -1;
}
memset((void*)&server,0,sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX;
strcpy(server.sun_path, "/tmp/testsocketUNIX");
unlink(server.sun_path);
if(bind(listenfd, (struct sockaddr*)&server, sizeof(server)) < 0)
{
perror("fail to bind");
return NULL;
}
........ /*省略后续socket api函数配置*/