UNIX Network Programming笔记之第四章

    本章主要讲述socket相关函数, fork和exec函数, getsockname和getpeername函数, 以及使用fork构建简单的并发服务器.
    socket函数, 创建一个套接字描述符
int socket(int family, int type, int protocol);
成功返回socket套接字描述符, 失败返回<0
family参数可以使用以下几种:
AF_INET    IPv4协议
AF_INET6   IPv6协议
AF_LOCAL   Unix域协议
AF_ROUTE   路由套接字
AF_KEY     密钥套接字
type参数
SOCK_STREAM 字节流套接字
SOCK_DGRAM 数据报套接字
SOCK_SEQPACKET 有序分组套接字
SOCK_RAW 原始套接字
protocol为family和type组合的值,如果为0, 则为组合默认值


connect函数, 与服务器建立连接
int connect(int sockfd, struct sockaddr *servaddr, socklen_t addrlen);
成功返回0, 出错返回-1
client调用connect前不需要进行bind, 当调用connect函数时, 会触发三路握手发送SYN, 连接建立成功或失败时connect函数才返回, 当返回出错有以下几个原因:
a. client没有收到SYN分节的响应, 返回ETIMEOUT错误.
b. 对client响应RST, 表明在指定的端口上没有进程在等待与它连接
c. SYN分节在路由上引发"destination unreachable" 


bind函数, 将本地协议地址赋予一个套接字
int bind(int sockfd, struct sockaddr *servaddr, socklen_t addrlen);
成功返回0, 出错返回-1
bind函数在服务器上调用,bind常见的一个错误是Address already in use. 如果未指定特定的IP或端口, 则由内核选择IP或端口. 


listen函数, 将套接字指定为监听套接字
int listen(int sockfd, int backlog);
成功返回0, 出错返回-1
当socket函数创建一个套接字,被假设为主动套接字, 意味着由client发起连接使用, 而调用listen之后, 由未连接的套接字变为监听套接字, 状态由CLOSED变为LISTEN. 而listen第二参数为未连接队列(SYN_RCVD)的长度+已连接队列(ESTABLISHED)的长度的最大值.通常不要设为0.要是一个SYN分节到达, 而这两个队列是满的,则忽略该分节, 意味着当时无法处理, client会重新发送该分节. 要是返回RST, client就无法区分是因为队列满了导致的,还是该端口无连接进程导致的.


accept函数, 从已连接队列列头返回一个已连接的套接字
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
成功返回0, 出错返回-1
accept函数将有3个可返回的值: 函数结果值, 客户端协议地址, 以及地址长度. 该函数的第一个参数是监听套接字.如果第二,三参数为NULL,表明对client身份不感兴趣.
服务器调用accept时, 将返回一个已连接的套接字, 要是已连接队列为空,则阻塞(默认socket是阻塞的).


close函数
int close(int sockfd);
成功返回0, 出错返回-1
clise函数并不会引发tcp关闭的四次握手, 只是导致相应的描述符的引用计数减一


fork与exec函数
pid_t fork();
在子进程中返回0, 父进程中返回子进程ID, 出错返回-1
而exec函数有6个, 区别在与待执行的程序文件是filename还是pathname;新程序的参数;把调用进程的环境变量传递给新程序还是为新进程指定新的环境变量
int execl(const char *pathname, const char *arg0, .../*(char *)0*/);
int execv(const char *pathname, char * const argv[]);
int execle(const char *pathname, const char *arg0, .../*(char *) 0, char * const envp[]*/)
int execve(const char *pathname, char *const argv[], char *const envp[]);(这是系统调用)
int execlp(const char *filename, const char *arg0, .../*(char *)0*/);
int execvp(const char *filename, char * const argv[]);
这些函数只有在出错时才返回到调用者, 否则控制将被传递给新程序的起始点.


getsockname和getpeername函数
返回与socket关联的协议地址信息
int getsockname(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);


一个简单的并发服务器
...
listenfd = socket(...)
bind(listenfd, ...)
listen(listenfd, ...)
for(;;){
connfd  = accept(listenfd, ...);
if(fork() == 0){
close(lisenfd); // 关闭监听socket
doit(connfd);   // 处理请求
close(connfd);  // 关闭连接socket
exit(0); // 子进程结束
}
close(connfd);
}
当调用fork后, 子进程克隆父进程信息, 监听socket和连接socket的引用计数为2, 子进程关闭监听socket, 将监听socket的引用计数变为1, 处理完成后关闭连接socket, 而父进程返回的时候关闭连接socket, 最后连接套接字引用计数为0, 引发tcp连接关闭的四分组序列. 
子进程处理与客户的连接, 父进程则可以监听套接字上再次调用accept来处理下一个客户连接.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值