struct sockaddr和struct sockaddr_i:
struct sockaddr和struct sockaddr_in这两个结构体用来保存一个套接字的信息。
在各种系统调用或者函数中,只要和网络地址打交道,就得用到这两个结构体。
网络中的地址包含3个方面的属性:
1 地址类型: ipv4还是ipv6
2 ip地址
3 端口
函数原型:
struct sockaddr
{
unsigned short int sa_family; //指定通信地址类型,如果是TCP/IP通信,则值为AF_inet
char sa_data[14]; //最多用14个字符长度,用来保存IP地址和端口信息
};
struct sockaddr_in
{
unsigned short int sin_family; //指定通信地址类型
uint16_t sin_port; //套接字使用的端口号
struct in_addr sin_addr; //需要访问的IP地址
unsigned char sin_zero[8]; //未使用的字段,填充为0
};
这两个结构体一样大,都是16个字节,而且都有family属性,不同的是:
sockaddr用其余14个字节来表示sa_data,而sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero
分别表示端口、ip地址。sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小。
sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别:
程序员不应操作sockaddr,sockaddr是给操作系统用的
程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。
一般的用法为:
程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数
用法事例:
1 int sockfd;
2 struct sockaddr_in servaddr;
3
4 sockfd = Socket(AF_INET, SOCK_STREAM, 0);
5
6 /* 填充struct sockaddr_in */
7 bzero(&servaddr, sizeof(servaddr));
8 servaddr.sin_family = AF_INET;
9 servaddr.sin_port = htons(SERV_PORT);
10 inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
11
12 /* 强制转换成struct sockaddr */
13 connect(sockfd, (struct sockaddr *) &servaddr, sizeo
将网络地址转换成长整型
long inet_addr(char *cp); //cp表示一个IP地址字符串;
将长整型IP地址转换成网络地址即大小端模式转换:
char *inet_ntoa(struct in_addr in);
创建套接字函数:
int socket(int domain,int type,int protocol);
//参数domain用于指定创建套接字所使用的协议族(可取AF_UNIX,AF_INET,AF_INTE6)
//参数type指定套接字的类型(可取SOCK_STREAM,SOCK_DGRAM,SOCK_RAW)
//参数protocol通常设置为0
在指定套接字上创建链接函数:
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
//参数sockfd是一个由函数socket创建的套接字
//参数serv_addr是一个地址结构,指定服务器的IP地址和端口号
//参数addrlen为参数serv_addr的长度
将一个套接字和某个端口绑定在一起的函数:
int bind(int sockfd,struct sockaddr *my_addr,socklen_t addrlen);
//一般只有服务器端的程序调用,参数my_addr指定了sockfd将绑定到的本地地址,可以将参数my_addr的sin_addr设置为INADDR_ANY而不是某个确定IP地址就可以绑定到任何网络接口。
把套接字转化为被动监听函数:
int listen(int s,int backlog);
//参数s为套接字,参数backlog指定链接请求队列的最大长度;
接收连接请求函数:
int accept(int s,struct sockaddr *addr,socklen_t *addrlen);
//参数s是由函数socket创建,经函数bind绑定到本地某一端口上,然后通过函数listen转化而来的监听套接字
//参数addr用来保存发起连接请求的主机的地址和端口
//参数addrlen是addr所指向的结构体的大小
在TCP套接字上发送数据函数:
ssize_t send(int s,const void *msg,size_t len,int flags);
//函数只能对处于连接状态的套接字使用,参数s为已建立好连接的套接字描述符,即accept函数的返回值
//参数msg指向存放待发送数据的缓冲区
//参数len为待发送数据的长度,参数flags为控制选项,一般设置为0
在TCP套接字上接收数据函数:
ssize_t recv(int s,void *buf,size_t len,int flags);
//函数recv从参数s所指定的套接字描述符(必须是面向连接的套接字)上接收数据并保存到参数buf所指定的缓冲区
//参数len则为缓冲区长度,参数flags为控制选项,一般设置为0
在UCP套接字上发送数据函数:
ssize_t sendto(int s,const void *msg,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);
//函数功能与函数send类似,但函数sendto不需要套接字处于连接状态,所以该函数通常用来发送UDP数据,同时因为是无连接的套接字,在使用sendto时需要指定数据的目的地址,参数msg指向待发送数据的缓冲区。
//参数len指定了待发送数据的长度
//参数flags是控制选项,含义与send函数中的一致
//参数to用于指定目的地址,目的地址的长度由tolen指定
在UDP套接字上接收数据函数:
ssize_t recvfrom(int s ,void *buf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen);
//与函数recv功能类似,只是函数recv只能用于面向连接的套接字,而函数recvfrom没有此限制,可以用于从无连接的套接字上接收数据
//参数buf指向接收缓冲区
//参数len指定了缓冲区的大小
//参数flags是控制选项,含义与recv中的一致
//如果参数from非空,且该套接字不是面向连接的,则函数recvfrom返回时,参数from中将保存数据的源地址
//参数fromlen在调用recvfrom前为参数from的长度,调用recvfrom后将保存from的实际大小
关闭套接字函数:
int close(int fd);
//参数fd为一个套接字描述符;
多路复用函数:
int select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
//参数n是需要监视的文件描述符数
//参数readfds指定需要监视的可读文件描述符集合
//参数writefds指定需要监视的可写文件描述符集合
//参数exceptfds指定需要监视的异常文件描述符的集合
//参数timeout指定了阻塞的时间