1. sys/types.h 数据类型定义
2. sys/socket.h 提供socket函数及数据结构
3. netinet/in.h 定义数据结构 sockaddr_in
4. arpa/inet.h 提供IP地址转换函数
5. netdb.h 提供设置及获取域名的函数
6. sys/ioctl.h 提供对I/O控制的函数
7. sys/poll.h 提供socket等待测试机制的函数
其他在网络程序中常见的头文件
1. unistd.h 提供通用的文件,目录,程序及进程操作的函数
2. errno.h 提供错误号errno的定义,用于错误处理
3. fcntl.h 提供对文件控制的函数
4. time.h 提供有关时间的函数
5. crypt.h 提供使用DES加密算法的加密函数
6. pwd.h 提供对/etc/passwd文件访问的函数
7. pthread.h 提供多线程操作的函数
8. signal.h 提供对信号操作的函数
9. sys/wait.h、sys/ipc.h、sys/shm.h 提供进程等待、进程间通讯(IPC)及共享内存函数
struct sockadd{
unsigned short sa_family;
char sa_data[14];
}
此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。但一般编程中并不直接针对此数据结构进行操作,而是使用另一个与sockaddr等价的数据结构sockaddr_in。
//sa_family成员是地址族类型(sa_family_t)的变量,地址族类型通常与协议族类型对应。常见的协议族(protocol family,也称domain)
//============================================
// 协议族 | 地址族 | 描述
// PF_UNIX | AF_UNIX | UNIX本地域协议族
// PF_INET | AF_INET | TCP/IPv4协议族
// PF_INET6 | AF_INET6 | TCP/IPv6协议族
//============================================
宏PF_* 和AF_* 都定义在bits/socket.h头文件中,且后者与前者有完全相同的值,所以二者通常混用。
//sa_data成员用于存放socket地址值。但是不同协议族的地址值具有不同的含义和长度。
//====================================================================
// 协议族 | 地址值含义和长度
// PF_UNIX | 文件的路径名,长度可达到108字节
// PF_INET | 16bit端口号和32bit IPv4地址,共6字节
// PF_INET6 | 16bit端口号,32bit流标识,128bit IPv6地址,32bit范围ID,共26字节
//====================================================================
struct sockaddr_in{
short int sin_family; //指代协议族,在socket编程中通常用AF_INET
unsigned short int sin_port; //存储端口号(使用网络字节顺序,网络字节顺序,采用统一的big endian排序,即数据的高字节,保持在内存的低地址中。)
struct in_addr sin_addr; //存储IP地址,使用in_addr这个数据结构。
unsigned char sin_zero[8]; //是为了让sockaddr和sockaddr_in两个数据结构保持大小相同而保留的空字节。
}
typedef struct in_addr{
unit{
struct{
unsigned char s_b1,s_b2,s_b3,s_b4;
}S_un_b;
struct{
unsigned short s_w1, s_w2;
}S_un_w;
unsigned long s_addr;
}S_un;
}IN_ADDR;
in_addr 的含义就是用共同体存储ip的三种表达:
1. 用四个字节表示IP地址的四个数字;
2. 用两个双字节来表示IP地址;
3. 用一种长整形来表示IP地址;
给 in_addr赋值的最简单方法是使用inet_addr函数,sockaddr_in.sin_addr.s_addr=inet_addr("192.168.1.1");
//常用函数如下:
1. 转换函数
unsigned long net_addr(const char *cp);
char * inet_ntoa(struct int_addr in);
2. 字节序转换函数
htons() 主机字节序转网络字节序 short
htonl() 主机字节序转网络字节序 long
ntonl() 网络字节序 short 转主机字节序
ntonl() 网络字节序 long 转主机字节序
3. 取得本地主机名
int gethostname(char *hostname, size_t size);
4. 取得本地信息
int getsockname(int sockfd, struct sockaddr* addr, int *addrlen);
5. 取得socket的对方地址
int getpeername(int s,struct sockaddr *name,socklen_t *namelen);
6. 获取DNS信息struct hostent *gethostbyname(const char* name);
struct hostent *gethostbyaddr(const char* addr, int len,int type);
7. 读取或改变socket属性
int getsockopt(int sockfd,int level, int name, char* value,int * optlen);
int setsockopt(int sockfd,int level,int optname, const void* optval,socklen_t optlen);
8. 轮询
int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout);
// 参数一: 最大文件描述符+1(本参数忽略,仅起到兼容作用);
// 参数二: 指向一组等待可读性检查的套接口;(是否有数据读)
// 参数三: 指向一组等待可写性检查的套接口;
// 参数四: 指向一组等待错误检查的套接口;
// 参数五: 轮询等待的最多时间,对于阻塞操作则为NULL;
// FD_CLR(int fd,fd_set *set);用来清除描述set中相关fd的位
// FD_ISSET(int fd,fd_set* set);用来测试描述词组set中相关fd的位是否为真
// FD_SET(int fd, fd_set *set); 用来设置描述词组set中的相关fd的位
// FD_ZERO(fd_set *set);用来清除描述词组set的全部位
通常情况下服务器端socket的步骤如下:
1. 建立socket
TCP: sockfd = socket(AF_INET,SOCK_STREAM, 0);
UDP: sockfd = socket(AF_INET,SOCK_DGRAM, 0);
2. 绑定端口
int bind(int sockfd,struct sockaddr *sa, int addrlen);
3. 监听端口
int listen(int sockfd,int queue_length);
4. 响应请求
int accept(int socked,struct sockaddr* addr, int *addrlen);
5. 关闭连接
int close(int sockfd);当进程结束时,系统内核会调用close(fd)关闭文件描述符,最好是手动调用函数关闭,否则长期以往会占用大量的文件描述符和系统资源(比如 说:长期的网络服务器)
int shutdown(int sockfd,int how) 是专门针对socket的操作函数,how可以取0--禁止接收、1--禁止发送、2--禁止收发。
//==============================================================================================================
// 可选值 | 含义
// SHUT_RD | 关闭sockfd上读的这一半,应用程序不能再针对socket文件描述符进行读操作,并且该socket接收缓冲区中的数据都被丢弃
// SHUT_WR | 关闭sockfd上写的这一半,socket的发送缓冲区中的数据会再真正关闭连接之前全部发送出去,应用程序不可再对该socket
// | 文件描述符执行写操作,这中国情况下,连接处于半关闭状态
// SHUT_RDWR | 同时关闭sockfd上的读和写
//==============================================================================================================
通常情况下客户端socket的步骤如下:
1. 建立socket
TCP: sockfd = socket(AF_INET,SOCK_STREAM,0);
UDP: sockfd = socket(AF_INET,SOCK_DGRAM,0);
// address.sin_family = AF_INET; //add
// inet_pton(AF_INET, ip, &address.sin_addr);
// address.sin_port = htons(port);