TCP/IP和网络编程
TCP/IP协议
互联网的基础。
TCP代表传输控制协议,IP代表互联网协议。
TCP/IP的组织结构分为几个层级,通常称为TCP/IP堆栈。如图所示为 TCP/IP 的各个层级以及每一层级的代表性组件及其功能。
进程与主机之间的传输层或其上方的数据传输只是逻辑传输。实际数据传输发生在互联网(IP)和链路层,这些层将数据包分成数据帧,以便在物理网络之间传输。下图所示为 TCP/IP 网络中的数据流路径。
IP主机和地址
主机是支持TCP/IP协议的计算机设备。每个主机由一个32位的IP地址来标识。
IP地址分为两部分,即 NetworkID 字段和HostID字段。根据划分,IP 地址分为A~E 类。例如,一个B类IP地址被划分为一个16位NetworkID,其中前2位是10,然后是一个16位的 HostID字段。发往IP地址的数据包首先被发送到具有相同 networkID的路由器。路由器将通过 HostID 将数据包转发到网络中的特定主机。每个主机都有一个本地主机名localhost,默认 IP地址为 127.0.0.1。本地主机的链路层是一个回送虚拟设备,它将每个数据包路由回同一个localhost。这个特性可以让我们在同一台计算机上运行TCP/IP 应用程序,而不需要实际连接到互联网。
IP协议
IP协议用于在 IP主机之间发送/接收数据包。IP尽最大努力运行。IP 主机只向接收主机发送数据包,但它不能保证数据包会被发送到它们的目的地,也不能保证按顺序发送。这意味着IP 并非可靠的协议。必要时,必须在IP 层的上面实现可靠性。下图所示是IP头格式:
UDP/TCP
-
UDP(用户数据报协议)在IP上运行,用于发送/接收数据报。与IP类似,UDP不能保证可靠性,但是快速高效。它可用于可靠性不重要的情况。
-
TCP(传输控制协议)是一种面向连接的协议,用于发送/接收数据流。TCP也可在IP 上运行,但它保证了可靠的数据传输。通常,UDP类似于发送邮件的USPS,而TCP类似于电话连接。
端口信号
应用程序 =(主机 IP,协议,端口号)
其中,协议是TCP或 UDP,端口号是分配给应用程序的唯一无符号短整数。要想使用UDP或 TCP,应用程序(进程)必须先选择或获取一个端口号。根据计算机网络课程中讲述的内容,端口可以分为公认端口(0-1023)、注册端口(1024-49151)、私有端口(49152-65535)。
应用程序可以选择一个可用端口号,也可以让操作系统内核分配端口号。
TCP/Ip网络中的数据流
图中,应用程序层的数据被传递到传输层,传输层给数据添加一个TCP或UDP 报头来标识使用的传输协议。合并后的数据被传递到IP 网络层,添加一个包含 IP地址的IP 报头来标识发送和接收主机。然后,合并后的数据再被传递到网络链路层,网络链路层将数据分成多个帧,并添加发送和接收网络的地址,用于在物理网络之间传输。IP地址到网络地址的映射由地址解析协议(ARP)执行(ARP1982)。在接收端,数据编码过程是相反的。每一层通过剥离数据头来解包接收到的数据、重新组装数据并将数据传递到上一层。发送主机上的应用程序原始数据最终会被传递到接收主机上的相应应用程序。
套接字编程
(1)套接字地址
struct sockaddr_in {
sa_family_t sin_family; // AF_INET for TCP/IP
// port number
in_port_t sin_port;
struct in_addr sin_addr;// IP address );
// internet address struct in_addr {
// IP address in network byte order
s_addr;
uint32_t
);
(2)套接字API
服务器必须创建一个套接字,并将其与包含服务器IP 地址和端口号的套接字地址绑定。它可以使用一个固定端口号,或者让操作系统内核选择一个端口号(如果 sin port为0)。为了与服务器通信,客户机必须创建一个套接字。对于UPD套接字,可以将套接字绑定到服务器地址。如果套接字没有绑定到任何特定的服务器,那么它必须在后续的 sendto()/recvfrom()调用中提供一个包含服务器IP 和端口号的套接字地址。
Web和CGI编程
万维网(WWW)或 Web 是互联网上的资源和用户组合,它使用超文本传输协议(HTTP)(RFC2616 1999)进行信息交换。自 20世纪 90年代初问世以来,随着互联网能力的不断扩展,Web 已经成为世界各地人们日常生活中不可或缺的一部分。因此,对于计算机科学的学生来说,了解这项技术非常重要。在本节中,我们将介绍 HTTP和Web编程的基础知识。Web 编程通常包括Web开发中涉及的编写、标记和编码,其中包括Web 内容、Web 客户机和服务器脚本以及网络安全。狭义上,Web编程指的是创建和维护 Web 页面。Web编程中最常用的语言是HTML、XHTML、JavaScript、Perl5和 PHP。
Web界面
Web 页面是用HTML标记语言编写的文件。Web文件通过一系列HTML元素指定Web 页面的布局,可在 Web 浏览器上解释和显示。常用的Web 浏览器有Internet Explorer、Firefox、Google Chrome 等。创建 Web 页面相当于使用HTML 元素作为构建块创建文本文件。与其说它是编程,不如说是文书类工作。因此,我们不讨论如何创建Web 页面。相反,我们将只使用一个示例 HTML文件来说明Web 页面的本质。下面给出了一个简单的HTML Web 文件。
实践
#include <stdio.h>
#include <arpa/inet.h>//inet_addr() sockaddr_in
#include <string.h>//bzero()
#include <sys/socket.h>//socket
#include <unistd.h>
#include <stdlib.h>//exit()
#define BUFFER_SIZE 1024
int main() {
char listen_addr_str[] = "0.0.0.0";
size_t listen_addr = inet_addr(listen_addr_str);
int port = 8080;
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t addr_size;
char buffer[BUFFER_SIZE];//缓冲区大小
int str_length;
server_socket = socket(PF_INET, SOCK_STREAM, 0);//创建套接字
bzero(&server_addr, sizeof(server_addr));//初始化
server_addr.sin_family = INADDR_ANY;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = listen_addr;
if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
printf("绑定失败\n");
exit(1);
}
if (listen(server_socket, 5) == -1) {
printf("监听失败\n");
exit(1);
}
printf("创建tcp服务器成功\n");
fd_set reads,copy_reads;
int fd_max,fd_num;
struct timeval timeout;
FD_ZERO(&reads);//初始化清空socket集合
FD_SET(server_socket,&reads);
fd_max=server_socket;
while (1) {
copy_reads = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 5000;
//无限循环调用select 监视可读事件
if((fd_num = select(fd_max+1, ©_reads, 0, 0, &timeout)) == -1) {
perror("select error");
break;
}
if (fd_num==0){//没有变动的socket
continue;
}
for(int i=0;i<fd_max+1;i++){
if(FD_ISSET(i,©_reads)){
if (i==server_socket){//server_socket变动,代表有新客户端连接
addr_size = sizeof(client_addr);
client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &addr_size);
printf("%d 连接成功\n", client_socket);
char msg[] = "恭喜你连接成功";
write(client_socket, msg, sizeof(msg));
FD_SET(client_socket,&reads);
if(fd_max < client_socket){
fd_max=client_socket;
}
}
else{
memset(buffer, 0, sizeof(buffer));
str_length = read(i, buffer, BUFFER_SIZE);
if (str_length == 0) //读取数据完毕关闭套接字
{
close(i);
printf("连接已经关闭: %d \n", i);
FD_CLR(i, &reads);//从reads中删除相关信息
} else {
printf("%d 客户端发送数据:%s \n", i, buffer);
write(i, buffer, str_length);//将数据发送回客户端
}
}
}
}
}
return 0;
}