UNP v1 第一章:简介
2015-10-03 15:14
543 查看
前言
开坑作死小能手在csapp的大坑才刚开始的情况下又来开个新坑了。。。不想像csapp那坑那样作死了。。。此篇以代码为主。。。UNIX网络编程分为两卷,分别为套接字联网API和进程间通信。
1.1 概述
编写网络通信程序前,需要确定程序通信所用的协议。大多数的网络应用是分为客户(Client)和服务器(Server)来组织的,通常称之为C/S结构。在书中使用一个头文件unp.h,其中包括了需要的头文件、一些错误处理函数以及包裹函数。1.2 时间获取客户端
下面实现了一个查询当前时间的客户端程序。客户端与服务器建立一个TCP连接后,服务器返回当前的时间与日期。#include "unp.h" int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) { // 使用argv[1]参数传递服务器端地址。 err_quit("Usage: daytimetcpcli <IPaddress>"); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { // 创建网际(AF_INET)字节流(SOCK_STREAM)套接字。 err_sys("socket error"); } bzero(&servaddr, sizeof(servaddr)); // 使用bzero将servaddr清零,也可使用memset(),但memset带3个参数容易犯错。 servaddr.sin_family = AF_INET; // 将地址族置为AF_INET。 servaddr.sin_port = htons(13); // 服务器端端口号设为13。 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { // 将点分十进制的参数转换为合适的格式,并设置为服务器端的地址。 err_quit("inet_pton error for %s", argv[1]); } if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) { // 与指定的服务器端建立一个TCP连接。SA是一个宏定义,等同于struct sockaddr。 err_sys("connect error"); } while ((n = read(sockfd, recvline, MAXLINE)) > 0) { // 读取服务器的应答,写入recvline中。当返回值为0(对端关闭连接)或为负(发送错误)时结束循环。 recvline = 0; if (fputs(recvline, stdout) == EOF) { err_sys("fputs error"); } } if (n < 0) { err_sys("read error"); } exit(0); }
在上述代码中用到的err_开头的函数即书中提供的错误处理函数,如
err_quit("inet_pton error for %s", argv[1])等同于
fprintf(stderr, "inet_pton error for %s", argv[1]); exit(1);
1.3 时间获取服务器端
#include "unp.h" #include <time.h> int main(int argc, char **argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 将地址设为INADDR_ANY目的是若服务器主机有多个网络接口,服务器程序可以在任意一个接口上接受客户连接。 servaddr.sin_port = htons(13); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); // 将端口捆绑到服务器进程的套接字。 Listen(listenfd, LISTENQ); // 将套接字转换为监听套接字,常量LISTENQ是允许这个监听描述符排队的最大客户连接数。 for ( ; ; ) { connfd = Accept(listenfd, (SA *) NULL, NULL); // 接受客户连接 ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); Write(connfd, buff, sizeof(buff)); // 发送应答 Close(connfd); // 关闭与客户的连接 } }
在服务器端程序中使用了包裹函数,它将错误处理函数包含进其中,缩短了程序的长度。如
Socket(),它的实现如下
int Socket(int family, int type, int protocol) { int n; if ((n = socket(family, type, protocol)) < 0) { err_sys("socket error"); } return(n); }
1.4 IPv6版本
上述的两个程序均为IPv4版本,通过稍微修改即可成为IPv6的版本。在IPv4中,地址族为AF_INET,IPv6中则要修改为AF_INET6。除地址族外,将
struct sockaddr_in改为
struct sockaddr_in6,并将该结构变量中的
sin_开头的成员修改为
sin6_。除此外,在服务器程序中,需将
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);修改为
servaddr.sin6_addr = in6addr_any。
1.5 效果
编译客户端与服务器端后在命令行中进行测试,因为我的客户端与服务器端都在同一台电脑上,所以用了环回地址127.0.0.1以及IPv6的换回地址::1作为参数。先启动服务器端,服务器端开启时需要有root权限,下面分别为IPv4与IPv6的服务器端。$sudo ./daytimetcpsrv $sudu ./daytimetcpsrvv6
开启服务器端后即可使用客户端程序获取当前时间。
$./daytimetcpcli 127.0.0.1 Sat Oct 3 14:22:04 2015 $./daytimetcpcliv6 ::1 Sat Oct 3 14:23:38 2015
相关文章推荐
- 多个生产者与多个消费者的问题
- UNP学习记录---三次握手和四次挥手
- UNP---套接字简介
- 接收到的数据,发出的数据,ip地址(Fit fot iOS,OS X)
- unix网络编程1 基础知识
- unix网络编程2 读写函数介绍
- 【Unix 网络编程】服务器网络编程模型——I/O复用:select 函数
- Unix网络编程之环境搭建
- Unix网络编程之基本TCP套接字编程
- unix网络编程学习心得(1)——未完成连接、已完成连接队列
- UNIX网路编程(第三版) 关于源代码的使用问题
- Unix网络编程第三版源码编译
- Unix网络编程 高级IO套接字设置超时
- Linux下 IP_RECVDSTADDR undefined 问题
- 进程间通信——FIFO
- 进程间通信——Posix消息队列
- 同步——互斥锁
- 同步——条件变量
- 同步——读写锁
- 同步——记录锁