网络编程(自制服务器)

    现在的网络应用随处可见,无法想象生活离开了网络会变得怎样,最常见的就是通过浏览器上网,在地址栏输入 URL 敲击回车,然后浏览器就呈现出相应的页面。虽然现在的网络应用五花八门,但是它们都是基于相同的编程模型,依赖相同的编程接口。

    每个网络应用都是基于客户端-服务端编程模型的,采用这个模型,一个应用是由一个服务器进程和一个或多个客户端进程组成。由服务器管理着某种资源,通过操作这些资源来为客户端提供某种服务。需要注意的是,客户端和服务端是进程,而不是我们常提到的机器或主机。

    在系统级 I/O中已经提到过,网络也仅仅是一种文件而已。一个插到 I/O 总线扩展槽的网络适配器提供了网络的物理接口。从网络上接收到的数据从适配器经过 I/O 和内存总线复制到内存;当然数据也同样可以从内存复制到网络。

    互联网络由大大小小的局域网和广域网组成,这些局域网和广域网可能采用了完全不同和不兼容的技术,互联网络需要解决的问题是把这些不兼容的网络连接起来。解决办法就是一层运行在每台机器和路由器上的协议软件,这种解决方案随处可见,比如 java 之所以夸平台是因为不同平台上的 JVM 做了适配。

    这个协议软件控制路由器和主机如何协同工作来实现数据传输,在命名上定义一种统一的主机地址格式消除差异;在数据传输上通过定义一种把数据位捆绑成不连续的片(包)来消除差异,一个包由包头和有效载荷组成,包头包括包的大小以及源主机和目的主机的地址,有效载荷包括从源主机发出的数据位。

    全球 IP 因特网是最著名最成功的互联网络实现,每台因特网主机都运行实现了 TCP/IP 协议的软件。一个 IP 地址就是一个 32 位无符号整数,如下是 IP 网络程序存放的 IP 地址结构,它总是以大端法(网络字节顺序)存放。

struct in_addr {
    uint32_t s_addr;
}

    因特网客户端和服务器相互通信使用的是 IP 地址,但是这对于人们而言太不友好了,所以因特网定义了一组更加人性化的域名,并且定义了域名集合和 IP 地址集合之间的映射。在 1988 年之前,这个映射都是通过一个叫做HOSTS.TXT的文本文件来手工维护的,此后改为通过 DNS(域名系统)来维护。

    客户端和服务端通过在连接上发送和接收字节流来通信,这个连接是点对点的,并且是全双工的。一个套接字就是连接的一个端点,每个套接字都有相应的套接字地址,由一个因特网地址和一个 16 位整数端口组成。客户端套接字地址中的端口是内核自动随机分配的,称之为临时端口;而服务端通常是很著名的端口,比如 Web 服务器用 80 端口,电子邮件服务器用 25 端口,并且每个知名端口都有其相应的服务名。下面是套接字地址的结构。

// _in 是 internet 的缩写
struct scoket_in {
    uint16_t sin_family; // 协议族
    uint16_t sin_port; // 端口号
    struct sin_addr; // IP 地址
    unsigned char sin_zero[8]; // sizeof(struct sockaddr)
}

struct socketaddr {
    uint16_t sa_family;
    char sa_data[14];
}

    内核提供了编写网络应用所需要的函数,基本看函数名称就知道它的功能了,下面展示了一小部分函数。

int socket(int domain, int type, int protocol);
int connect(int clientfd, const struct sockadd *addr,
            socklen_t addrlen);
int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int listenfd, struct sockaddr *addr,
            int *addrlen);

int getaddrinfo(const char *host, const char *service,
            const struct addrinfo *hints,
            struct addrinfo **result);
int getnameinfo(const struct sockaddr *sa, 
            socklen_t salen, char *host, size_t hostlen,
            char *service, size_t servlen, int flags);
............

    像用于转换二进制和字符串表示的getaddrinfo()getnameinfo()函数是可重入的。通过内核提供的这些函数就可以编写出自己的 web 服务器。

    可重入函数主要用于多任务系统中,简单讲就是可中断函数,在这个函数执行的任何阶段打断它,操作系统去调用另一段代码,再返回控制时不会出现什么错误。因为它除了使用自己栈上的变量外不依赖于任何环境。注意可重入函数不一定是线程安全的。

    最后是原书作者自己实现的一个 TINY Wed 服务器,用 C 实现的,在 linux 环境下编译运行即可访问,下图是这个服务器运行的效果,访问链接http://127.0.0.1:8000/cgi-bin/adder?3&7就可以得到运算结果。整个程序代码也不长,我准备好好看看再抄一遍,源码可访问链接

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guanngxu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值