初识linux socket编程

csapp.h的配置

在编译链接hex2dd.c时,报错 undefined reference to 'unix_error' collect2: error: ld returned 1 exit status
于是我擅自把unix_error的内容拿出来写进hex2dd.c。而在dd2hex.c中,apt_error和unix_error两个函数都报错。
我决定解决这个错误。通过生成动态库的方法:
首先依次执行

// 当前路径下有csapp.h和csapp.c
gcc -shared -fpic -o libcsapp.so csapp.c -lpthread
sudo mv libcsapp.so /usr/local/lib

然后执行gcc dd2hex.c -o dd2hex -lcsapp并运行程序

发现编译没有报错,而运行时报错了。这是由于运行时链接没有找到动态库。
尝试来到/etc/ld.so.conf.d/下新建一个文件添加/usr/local/lib为寻找动态库的地址,发现已经有一个文件内含有这个地址

执行sudo ldconfig更新ld.so.cache,然后即可成功运行。

改变IP地址的表示形式

十六进制转为点分十进制

/*hex2dd.c*/
#include"csapp.h"
int main(int argc, char **argv)
{
    struct in_addr inaddr;  // 网络字节序
    uint32_t addr;   // 主机字节序
    char buf[MAXBUF];   // 点分十进制

    if (argc != 2)
    {
        fprintf(stderr, "usage: %s <hex number>\n", argv[0]);
        exit(0);
    }
    sscanf(argv[1], "%x", &addr);
    inaddr.s_addr = htonl(addr);

    if (!inet_ntop(AF_INET, &inaddr, buf, MAXBUF))
            fprintf(stderr, "%s: %s\n", "inet_ntop", strerror(errno));
    printf("%s\n", buf);

    exit(0);
}

编译命令:gcc hex2dd.c -o hex2dd(需要把csapp.h和csapp.c放在当前路径)
运行效果:

IP地址结构定义如下:

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

一个IP地址就是一个32位无符号整数,所以上述程序也可以改写成如下:

/*hex2dd_1.c*/
#include"csapp.h"
int main(int argc, char **argv)
{
    uint32_t inaddr;  // 网络字节序
    uint32_t addr;   // 主机字节序
    char buf[MAXBUF];   // 点分十进制

    if (argc != 2)
    {
        fprintf(stderr, "usage: %s <hex number>\n", argv[0]);
        exit(0);
    }
    sscanf(argv[1], "%x", &addr);
    inaddr = htonl(addr);

    if (!inet_ntop(AF_INET, &inaddr, buf, MAXBUF))
            fprintf(stderr, "%s: %s\n", "inet_ntop", strerror(errno));
    printf("%s\n", buf);

    exit(0);
}

sscanf函数指定了输入源为argv[1],以16进制格式输入;

#include<arpa/inet.b>
uint32_t htonl(uint32_t hostlong);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)

htonl函数把主机字节顺序转换为网络字节顺序;
inet_ntop函数将十六进制转换为点分十进制。

点分十进制转为十六进制

/*dd2hex.c*/
#include"csapp.h"
int main(int argc, char **argv)
{
    struct in_addr inaddr;
    int rc;
    uint32_t addr;

    if (argc != 2)
    {
        fprintf(stderr, "usage: %s <dotted-decimal>\n", argv[0]);
        exit(0);
    }

    rc = inet_pton(AF_INET, argv[1], &inaddr);
    if (rc == 0)
        app_error("inet_pton error: invalid dotted-decimal address");
    else if (rc < 0)
        unix_error("inet_pton error");
    
    printf("0x%x\n", ntohl(inaddr.s_addr));
    exit(0);
}

编译命令:gcc dd2hex.c -o dd2hex -lcsapp(提前配置好动态库)
运行效果:

#include <arpa/inet.h>
uint32_t ntohl(uint32_t netlong);
int inet_pton(int af, const char*src, void *dst);

ntohl函数把网络字节序转换为主机字节序;
inet_pton函数把点分十进制转换为十六进制。

将域名转换为IP地址

/*hostinfo.c*/
#include "csapp.h"
 
int main(int argc, char **argv)
{
    struct addrinfo *p, *listp, hints;
    char buf[MAXLINE];
    int rc, flags;

    if (argc != 2)
    {
        fprintf(stderr, "usage: %s <domain name>\n", argv[0]);
        exit(0);
    }

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET;  // 查找32的IP地址
    hints.ai_socktype = SOCK_STREAM;    // 用作连接的端点
    if ((rc = getaddrinfo(argv[1], NULL, &hints, &listp)) != 0) // service设为NULL
    {
        fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc));
        exit(1);
    }

    flags = NI_NUMERICHOST;
    for (p = listp; p; p = p->ai_next)
    {
        getnameinfo(p->ai_addr, p->ai_addrlen, buf, MAXLINE, NULL, 0, flags);
        printf("%s\n", buf);
    }

    freeaddrinfo(listp);

    exit(0);
}

运行效果:

/* Structure to contain information about address of a service provider.  */
struct addrinfo
{
  int ai_flags;			/* Input flags.  */
  int ai_family;		/* Protocol family for socket.  */
  int ai_socktype;		/* Socket type.  */
  int ai_protocol;		/* Protocol for socket.  */
  socklen_t ai_addrlen;		/* Length of socket address.  */
  struct sockaddr *ai_addr;	/* Socket address for socket.  */
  char *ai_canonname;		/* Canonical name for service location.  */
  struct addrinfo *ai_next;	/* Pointer to next in list.  */
};
#include<sys/types.h>
#include<sys/socket.h>
#include<netdb.h>
int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints,
                struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
const char *gai_strerror(int errcode);

getaddrinfo的host参数可以是域名也可以是数字地址,service参数可以是服务名也可以是十进制端口号。必须指定两者中至少一个。通过hints参数设置个别字段,ai_family设置为AF_INET会将列表限制为IPv4地址,设置为AF_INET6则限制为IPv6地址;ai_socketype设置为SOCK_STREAM将列表限制为对每个地址最多一个addrinfo结构;返回result,result是一个指向addrinfo结构的链表,其中每个结构指向一个对应于host和service的套接字地址结构。

参考

深入理解计算机系统(原书第3版)
csapp.h头文件的使用 ---- 3种方法运行《深入理解计算机系统》中的代码
Linux动态库的查找路径

posted @ 2020-12-20 15:48  平静的雨田  阅读(31)  评论(0编辑  收藏