linux C socket
socket套接字和管道同样可以提供进程内通信。但套接字更胜一筹,不同的进程可以跨越不同的主机(说白了,支持网络通信)。使用套接字的知名程序:telnet、rlogin、ftp等。
你需要知道的一些基本原理:
使用socket时必须指定通信的类型和协议。
通信类型决定了以下内容:1、数据传输的基本单元(有的会打包传输,有的作为一个结构体);2、数据是否可丢失,数据传输的可靠性:(有的数据丢失一两个便不可读了,有的数据如视频不受太大影响);3、是一对一的通信还是一对多的通信。
每一个socket必须有一个命名空间,命名空间一般以PF_开头,AF_开头的符号名指定命名空间的地址格式。
你需要了解的协议内容
要让两个socket能够通信,必须指定相同的协议。
协议和命名空间、通信风格是绑定的。比如TCP协议只能用在stream风格的通信中,Internet的命名空间中。对于指定的命名空间,请使用默认协议(不同的协议、命名空间组合可能会出错)
SOCKET几种通信风格:
int SOCK_STREAM: 提供可靠的通信方式。有连接。
int SOCK_DGRAM: 和SOCK_STREAM相反,使用的是不可靠通讯方式。无连接,每一个数据包都必须指定地址。一般来说,丢包的时候需要重发数据包。
int SOCK_RAW: 这种风格允许我们访问底层网络协议和接口,一般来讲我们不需要使用这种风格。
SOCKET地址:
实际上Socket地址就是socket的名字。在这里地址和名字是同义词。只有把地址和socket绑定后,我们才可以使用socket通信。
网络接口名:
每一个网络接口都有个名字如:lo、eth0。但在计算机内部,我们通过索引来使用接口。
#include <net/if.h> size_t IFNAMESIZE //常量:定义了保存接口名的最大buffer大小。(包含了最后zero 结束字节)
unsigned int if_nametoindex (const char *ifname) //通过接口名返回该接口的索引号,如果不存在该接口名则返回0。也可以用来判断一个接口是否存在。
char * if_indextoname (unsigned int ifindex, char *ifname) //将接口索引号ifindex和接口名ifname映射起来。如果索引无效,返回空指针,否则返回接口名。
struct if_nameindex //用来保存某个接口的信息。 /*成员如下:*/ unsigned int if_index //接口索引 char *if_name //接口名(以NULL结尾)
struct if_nameindex * if_nameindex(void) //返回一个if_nameindex结构体的数组(包含了所有的网络接口)。结束标志:接口索引为0,接口名为空指针。 //返回的结构体必须用if_freenameindex释放
void if_freenameindex (struct if_nameindex *ptr) //解释如上
本地命名空间:
符号名为PF_LOCAL。本地命名空间也被称之为Unix域套接字。另外一个名字是file namespace(socket 地址常被实现为文件名)
本地socket无法被其他机器连接(即便其他机器共享了包含本地socket的文件系统)。其他机器可以共享的文件系统查看到scoket文件,但无法连接。关闭本地命名空间的socket,建议同时删除socket文件。本地命名空间只支持一种协议(不管通信风格),协议号是0。
#include <sys/socket.h> int PF_LOCAL int PF_UNIX int PF_FILE
以上几个都是宏常量,而且都是同义词。这些常量用来指定本地命名空间。
#incude <sys/un.h> struct sockaddr_un { short int sun_family; char sun_path[108]; }
sun_family:标志了地址族和socket 地址的格式。在本地命名空间中,使用AF_LOCAL。
sun_path[108]:socket使用的文件名(很奇怪为何是108个字节,理查德·马修·斯托曼推荐使用alloca来申请适当大小的空间存储文件名。并将该数组长度设置为0)
int SUN_LEN(struct sockaddr_un *ptr)
SUN_LEN是宏定义的函数,用来计算本地命名空间中socket地址的长度。实际为结构体sockaddr_un的大小。
创建一个本地socket实例:
#include <stddef.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> int make_named_socket (const char *filename) { struct sockaddr_un name; int sock; size_t size; /* Create the socket */ sock = socket (PF_LOACAL, SOCK_DGRAM, 0); if (sock < 0) { perror ("socket"); exit (EXIT_FAILURE); } /*Bind a name to the socket.*/ name.sun_family = AF_LOCAL; strncpy (name.sun_path, filename, sizeof (name.sun_path)); name.sun_path[sizeof(name.sun_path)-1] ='\0'; /* The size of the address is the offset of the start of the filename, plus its length (not including the terminating null byte). Alternatively you can just do: size = SUN_LEN(&name); */ size = (offsetof (struct sockaddr_un, sun_path)) + strlen (name.sun_path)); if (bind (sock, (struct sockaddr_un *) &name, size) < 0) { perror ("bind"); exit (EXIT_FAILURE); } return sock; }