unp学习笔记
unp源码获取与编译
- 源码获取、解压流程如下:
wget http://www.unpbook.com/unpv13e.tar.gz
tar -zxvf unpv13e.tar.gz
- 源码编译,建议先查看源码中的 README
# README 部分内容如下:
QUICK AND DIRTY
===============
Execute the following from the src/ directory:
./configure # try to figure out all implementation differences
cd lib # build the basic library that all programs need
make # use "gmake" everywhere on BSD/OS systems
cd ../libfree # continue building the basic library
make
cd ../libroute # only if your system supports 4.4BSD style routing sockets
make # only if your system supports 4.4BSD style routing sockets
cd ../libxti # only if your system supports XTI
make # only if your system supports XTI
cd ../intro # build and test a basic client program
make daytimetcpcli
./daytimetcpcli 127.0.0.1
If all that works, you're all set to start compiling individual programs.
Notice that all the source code assumes tabs every 4 columns, not 8.
MORE DETAILS
......
- 按照源码流程编译
# 下面这两组命令一般不会出问题
cd unpv13e
./configure
cd lib
make
# 下面 make 可能会出现问题, 解决办法见 下方solve1
cd ../libfree
make
# libroute 一般不编译
cd ../libxti
make
# 通常可以直接 make 编译出可执行文件
cd ../intro
make daytimetcpcli
# 执行可能会出现 connect error: Connection refused
# 解决办法见 solve2
./daytimetcpcli 127.0.0.1
- solve1
错误提示的内容大概是:inet_ntop.c 中60行声明与原型申明 /usr/include/arpa/inet.h不匹配;
解决方法:
在 inet.h 中将 size_t 定义成 socklen_t,即在 /usr/include/arpa/inet.h 中添加下面这句话
// 添加在头文件之后
// inet.h为只读文件,需要sudo vim 或 sudo nano 才能修改
#define size_t socklen_t
- solve2
出现问题的原因可能是没安装 xinetd 或者 daytime 服务程序没开
# 安装 xinetd
sudo apt-get install xinetd
# 打开 daytime文件,将两个disable = yes 修改成 disable = no
vim /etc/xinetd.d/daytime
(修改...)
# 修改后许重启 xinetd 服务
service xinetd restart
时间获取客户端启动流程
程序的流程如下:
- 声明相关结构体、变量
- 创建网际字节流套接字
- 将工作类型、IP地址、端口号填入套接字(IP地址点分制转换成正确格式)
- 建立TCP连接
- 读取数据
- 输出数据
每个步骤的关键结构体、函数如下
step 1: struct sockaddr_in
step 2: socket(AF_INET, SOCKSTREAM, 0)
step 3: htons(端口号) //转换端口号
inet_pto(AF_INET, 源字符串地址, 目的字符串地址) //转换IP地址
step 4: connect(sockfd, IP地址的地址, ip地址的长度)
step 5: read(sockfd, 存放地址, 读取长度)
step 6: fputs(存放地址, stdout)
时间获取客户端
#include <stdio.h>
#include <unp.h>
void err_log(int numb, char * s);
int
main(int argc, char *argv[]){
/*声明变量、结构体*/
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in sadd;
//struct hostent * hp;
/*判断系统输入参数*/
if (argc != 2)
err_log(-1, "usage: get_server_time <IPaddress>");
/*socket创建一个网际(AF_INET) 字节流(SOCK_STREAM) 套接字*/
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_log(-2, "socket error");
/*初始化结构体*/
memset(&sadd, 0, sizeof(sadd));
/*将服务器的工作类型、IP地址与端口号填入网际套接字*/
sadd.sin_family = AF_INET;
sadd.sin_port = htons(13); // day time server
if (inet_pton(AF_INET, argv[1], &sadd.sin_addr) <= 0) // 将点分制ip转换成正确格式,递给 sockaddr_in 中的 sin_addr
err_log(-1, "inet_pton error for this ip");
/*build a TCP connect, and determine if the link is established*/
if (0 > connect(sockfd, (struct sockaddr *)&sadd, sizeof(sadd)))
err_log(-2, "connect error");
/*read data*/
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; // end of string
if (fputs(recvline, stdout) == EOF)
err_log(-2, "fputs error");
}
if (n < 0)
err_log(-2, "read error");
exit(0);
}
// 重写一遍之后,err_sys err_exit函数无法使用
// 只好自己写了一个err_log函数替代
// 在大型项目中,可以在这个函数中将错误写入log
void
err_log(int numb, char * s) {
printf("%s\n", s);
switch (numb)
{
case -1:
exit(-1);
case -2:
exit(-2);
default:
exit(numb);
}
}
测试结果
# 大概会输出这种形式的字符串
22 MAY 2019 21:06:40 CST