《UNIX网络编程》卷一第四章学习笔记
- 4.2 socket函数
#include<sys/socket.h>
int socket(int family,int type,int protocol);
//返回值:出错返回-1,否则返回非负描述符
famliy参数指明协议族:
取值 | 含义 |
---|---|
AF_INET | IPV4协议 |
AF_INET6 | IPV6协议 |
AF_LOCAL | Unix域协议 |
AF_ROUTE | 路由套接字 |
AF_KEY | 密钥套接字 |
type参数指明套接字类型:
取值 | 含义 |
---|---|
SOCK_STREAM | 字节流套接字 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 有序分组套接字 |
SOCK_RAW | 原始套接字 |
protocol参数指定传输协议:
取值 | 含义 |
---|---|
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
0 | 给定famliy和type组合的系统默认值 |
- 4.3 connect函数
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen);
//返回值 成功返回0,出错返回-1
sockfd是由socket函数返回的套接字描述符,后两个参数分别是一个指向套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的IP地址和端口号。
- 4.4 bind函数
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
//返回值 成功返回0,出错返回-1
bind函数把一个本地协议赋予一个套接字。参数和connect是一样的,但后两个可以都指定,也可以指定一个,也可以不指定。不指定的参数由内核进行配置。
- 4.5 listen函数
#include<sys/socket.h>
int listen(int sockfd,int backlog);
//成功返回0,出错返回-1
listen函数仅由TCP服务器调用,它做两件事情。
1)当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。
2)listen函数第二个参数规定了内核应为相应套接字排队的最大连接个数(也许实际会更大一些)。
本函数通常在socket和bind后accept前被调用。
- 4.6 accept函数
accept函数由TCP服务器调用,用于从已完成连接对楼队头返回下一个已完成连接。如果已连接队列为空,那么进程被置于休眠状态。
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
//成功返回非负描述符,出错返回-1
addrlen是值-结果参数:调用前将指针对应值为cliaddr地址结构的长度,返回时,指针对应值被修改为内核存放在该套接字地址结构内的确切字节数。
要区分accept函数返回的已连接套接字描述符和它的第一个参数监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字,当服务器完成对某个给定用户的服务时,相应的已连接套接字就被关闭。
如果对连接者的IP、端口号不感兴趣,accept函数的后两个参数可置为NULL。但好像不能只设置一个,否则会有一些问题。
- 4.7 fork和exec函数
这两个其实和网络编程没太大关系。前者比较重要的一点是父进程中调用fork前打开的所有描述符在fork返回后由子进程分享。后者不是很熟悉,手写shell才会用到吧…… - 4.8 并发服务器
举了个简单的并发例子。没有啥。 - 4.9 close函数
close函数在文件就涉及到,也没啥。
#include<unistd.h>
int close(int sockfd);
//成功返回0,出错返回-1
- 4.10 getsockname和getpeername函数
这两个函数或返回与某个套接字关联的本地协议地址,或返回与某个套接字相关的外地协议地址。
#include<sys/socket.h>
int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen);
int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addrlen);
//成功返回0,出错返回-1
通俗地讲,这两个函数的作用就是就是在第二个参数指向的套接字地址结构中填充本机IP地、址服务器进程所用端口号和地址族。顾名思义,getsockname自然就是返回自己这边的地址结构,getpeername则是返回对面的。
第一个函数,我觉得似乎没啥用……不过数组书中给出了getpeername的应用场景,看后觉得很有道理,复述如下:
当一个服务器是由调用1过accept的某个进程通过调用exec执行程序时,它能够获取客户身份的唯一途径便是调用getpeername。inetd fork并exec某个TCP服务器程序时就是如此情形。……inetd随后调用fork,派生除inetd的一个子进程。既然子进程起始于父进程内存镜像的一个副本,父进程中的那个套接字地质结构在子进程中也可用,那个已连接的描述符也是如此。然而当子进程调用exec执行真正的服务器程序时,子进程的内存映像被替换成新的服务器程序文件(也就是说包含对端地址的那个套接字地址结构就此丢失),不过那个已连接套接字描述符跨exec继续保持开放。服务器程序首先调用的函数之一便是getpeername,用于获取用户的IP地址和端口号。