选到了想上的网络编程课,用的书是非常著名的Unix Networking Programming Volume 1(Edition 3),那么实验自然是用书上的实验了。书上的实验基本上都引用了unp.h
的头文件,这个头文件是这本书编写的一种All in one的环境,并且在某些结构体未定义的情况下补充定义结构体等功能,该环境需要手动安装和配置,因此记录一下在配置过程,以及过程中出现的问题和解决办法。
系统环境
WSL2,发行版为Ubuntu 20.04LTS,其他WSL发行版以及原版Linux应该都差不多(未经过考证)
VSCode和插件Remote WSL
build-essential,使用apt安装
配置过程
-
下载unp库的源码,下载地址
-
解压并进入文件夹,按照README的步骤执行以下命令
./configure cd lib/ make cd ../libfree/ make
在
libfree/
中make的时候可能会遇到size_t
的报错解决办法是将
libfree/inet_ntop.c
的第60行size_t
改为socklen_t
即可在此过程中还可能会遇到一些warning,可能是因为使用的gcc-9和比较高版本的内核,和这个库有一点不太兼容,只要没有error就可以执行
-
make完成回到主目录下,发现已经编译好静态库
libunp.a
,将其复制到/usr/lib
和/usr/lib64
两个目录下,再将config.h
和key/unp.h
复制到/usr/include/unp
目录下(这里我新建了unp的目录为了方便管理,也可以直接放在/usr/include
目录下),并将unp.h
中的#include "../config.h"
改为#include "config.h"
(只要你的unp.h能找到config.h就好了) -
开始使用书上的例子作为测试
#include "unp.h" int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) { err_quit("usage: a.out <IPaddress>"); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { err_sys("socket error"); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(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) { err_sys("connect error"); } while ((n = read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = 0; if (fputs(recvline, stdout) == EOF) { err_sys("fputs error"); } } if (n < 0) { err_sys("read error"); } exit(0); }
执行以下编译命令
gcc cli.c -o cli -lunp
可能会出现以下报错
-
in_pktinfo
重复定义发现是
in_pktinfo
的结构体重复定义了,具体的定义位置是在/usr/include/x86_64-linux-gnu/bits/in.h
第157行,比较两者的定义可以发现,其中的变量名称都是相同的,那么直接替换应该没有问题,但是直接注释看上去又不太好(其实应该没问题),所以参考unp.h
中的其他代码风格,在config.h
中定义了一个宏#define HAVE_STRUCT_IN_PKTINFO 1
,然后将unp.h
中的结构体定义用#ifndef
框起来,如下图再次编译就没有这个错误了
-
err_sys
或err_print
没有定义这是因为该函数是单独写出来的,需要下载其他文件,为了避免节外生枝,笔者直接采用
printf()
和exit()
来显示并退出
-
至此,unp.h
环境搭建完毕,并且直接使用VSCode的Remote WSL到连虚拟机,C语言的插件也能识别到unp库的东西