选到了想上的网络编程课,用的书是非常著名的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库的东西