1. 数据结构
1.1
- sockaddr是通用的socket地址,sockaddr_in也是经常使用的地址,两者可以进行类型转换
struct sockaddr{
unsigned short sa_family; //address family, AF_xxx
char sa_data[14]; //14 bytes of protocol address
}
- sa_family是地址家族,一般都是"AF_xxx"的形式,通常大多用的是都是AF_INET,代表TCP/IP协议族
- sa_data是14字节协议地址
1.2 sockaddr_in
struct sockaddr_in{
short int sin_family; // Address family
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
}
- sin_family指代协议族,在socket编程中只能是AF_INET
- sin_port存储端口号(使用网络字节顺序)
- sin_addr存储IP地址,使用in_addr这个数据结构
- sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
- sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockaddr的结构体,并代替它
- 也就是说,你可以使用sockaddr_in建立你所需要的信息,然后用进行类型转换就可以了
1.3 in_addr
struct in_addr{
union{
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
}
1.4 例子:
int sockfd;
struct sockaddr_in my_addr; //声明一个sockaddr_in结构体my_addr
sockfd = socket(AF_INET, SOCK_STREAM, 0); //初始化一个sockfd
my_addr.sin_family = AF_INET; //协议
my_addr.sin_port = htons(MYPORT); //端口
my_addr.sin_addr.s_addr = inet_addr("192.168.0.1"); //网络地址,inet_addr将字符串转为十六进制,比如(0xC0A80001)
bzero(&(my_addr.sin_zero), 8); //填充空字节
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); //绑定
2.系统函数
2.1 socket
- socket()函数是任何套接口网络编程中第一个使用的函数,它向用户提供一个套接字,即套接口描述文件字,它是一个整数。如同文件描述符一样,是内核标识一个IO结构的索引。通过socket函数,我们指定一个套接口的协议相关的属性,为进行使用socket api做好准备
int socket(int family, int type, int protocol)
- 输入:
- family: 指定一个协议簇,也往往被称为协议域。系统存在许多可以的协议簇,常见有AF_INET──指定为IPv4协议,AF_INET6──指定为IPv6
- type: 这个参数指定一个套接口的类型,套接口可能的类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW等等,它们分别表明字节流、数据报、有序分组、原始套接口protocol 指定相应的传输协议,也就是诸如TCP或UDP协议等等,系统针对每一个协议簇与类型提供了一个默认的协议,我们通过把protocol设置为0来使用这个默认的值
- 返回:非负描述字──成功, -1──出错
2.2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
- 输入:
+sockfd: socket文件描述符,也是socket函数的返回值
+addr 构造出的IP地址加端口号
+addrlen sizeof(addr)长度
因为struct sockaddr* 是一个通用指针类型,addr实际上可以接受多种协议的sockaddr结构体,并且长度很肯能不一样,所以需要输入结构长度的参数
- 返回:
0——成功,-1——失败,
2.3 setsockopt
- setsockopt()用来设置参数s 所指定的socket 状态
int setsockopt( int socket, int level, int option_name,const void *optval, socklen_t optlen)
- 输入:
- socket: socket文件描述符,也是socket函数的返回值
- level: level 代表欲设置的网络层, 一般设成SOL_SOCKET 以存取socket 层
- option_name: 参数optname 代表欲设置的选项:
- SO_DEBUG 打开或关闭排错模式
- SO_REUSEADDR 允许在bind ()过程中本地地址可重复使用
- SO_TYPE 返回socket 形态.
- SO_ERROR 返回socket 已发生的错误原因
- SO_DONTROUTE 送出的数据包不要利用路由设备来传输
- SO_BROADCAST 使用广播方式传送
- SO_SNDBUF 设置送出的暂存区大小
- SO_RCVBUF 设置接收的暂存区大小
- SO_KEEPALIVE 定期确定连线是否已终止.
- SO_OOBINLINE 当接收到OOB 数据时会马上送至标准输入设备
- SO_LINGER 确保数据安全且可靠的传送出去
- optval: optval 代表欲设置的值
- optlen: optval 的长度
- 返回:
0——成功,-1——失败
2.4 getsockopt
- getsockopt()会将参数s 所指定的socket 状态返回
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen)
- 输入:
- sock: socket文件描述符,也是socket函数的返回值
- level: level 代表欲设置的网络层, 一般设成SOL_SOCKET 以存取socket 层
- optname: 代表欲取得何种选项状态
- optval: 指向欲保存结果的内存地址
- optlen: 为该空间的大小
- 返回:
2.5 listen()
int listen(int sockfd, int backlog)
- sockfd是被listen函数作用的套接字,sockfd之前由socket函数返回。在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。
- backlog定义内核监听队列的最大长度。APUE中指出,backlog只是一个提示,具体的数值实际上由系统决定
2.6 accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
- 输入:
- sockfd 上述listen函数指定的监听socket
- addr 请求连接方(即客户端)地址
- addrlen 客户端地址长度
- 返回:
- 函数执行成功返回一个新的连接socket,失败返回-1
2.7 connect
int connect(int sockfd, const struct sockaddr *server_addr, socklen_t *addrlen)
- 输入:
- sockfd socket函数返回一个socket
- server_addr 服务端地址
- addrlen 服务端地址地址长度
- 返回:
2.8
int close(int sockfd)