[1] 历史
1. 网络的产生
1957 Sputnik
1958 国防高级项目研究局(Defense Advanced Research Projects Agency 简称: DARPA)
1968 ARPAnet(NCP, 不支持不同OS、不同类型的计算机之间的通信, 不支持纠错)

2. TCP协议产生
1972 Robert Kahn被DARPA雇佣研究卫星和地面通信
1973 NCP协议的制定者Vinton Cerf也加入DARPA,研究封包交换网络
(ARPAnet 封包无线网 封包卫星网)
1973 Robert Kahn 和Vinton Cerf一起制定TCP(Transmission Control Protocol)
(支持不同OS 不同类型的计算机之间的通信 纠错)

3. TCP/IP协议产生
1974 实时性数据传输要求TCP(纠错 跨网络传输) = TCP(纠错) + IP(跨网络传输)
同时加入UDP(User Datagram Protocol)协议, 不支持纠错

什么实时性数据?
优酷看电影

为什么实时性数据传输要求TCP协议分解?
(1) 错误产生原因
TCP协议纠错(检测错误, 错误的包采取重传)---网络条件不好(重传数据包)--->数据传输延迟

(2) 解决办法
1. 可靠性要求比较高的数据(QQ文字)
TCP + IP

2. 实时性要求比较高的数据,可靠性要求向对不高(QQ视频)
UDP + IP

4. TCP/IP协议大规模使用
1983 BSD(unix)实现TCP/IP协议,随着BSD系统大规模使用,TCP/IP协议也得到了大规模使用

[2] 基本概念
1. 封包
对数据的一种封装,类似于"包裹"

2. 协议
通信双方预定好的数据包(封包)格式,类似于快递公司让我们在约定的地方填写“收货人电话和地址”

3. Internet(互联网)
连接网络的网络
(1) 早期


用户数据
(货物)
----------------------------------------------------------------
TCP(Transmission Control Protocol) UDP(User Datagram Protocol)
(包裹)
----------------------------------------------------------------
IP(Internet Protocol)
(集装箱)
----------------------------------------------------------------
ARPA网 封包无线网 封包卫星网
(公路网) (水路网) (航空网)

1. TCP(可靠)
面向连接(类似于电话,先拨通,才能说话)
纠错(保证数据不丢失、无错误、无失序、无重复到达)

2. UDP(不可靠)
不面向连接
不纠错

(2) 现在
主要框架和早期网络完全相同,主要的协议仍然采用TCP/IP协议,
但是向里面加入了很多其他协议,以适应新的应用需求。
具体协议见PPT
telnet 远程登录协议
FTP File Transfer Protocol
HTTP HyperText Transfer Protocol
DNS Domain Name System
SMTP Simple Mail Transfer Protocol
ICMP Internet Control Message Protocol
IGMP Internet Group Management Protocol
FDDI Fiber Distributor Data Interface
TFTP 简化版的FTP
PPP Point to Point Protocol
ARP Address Resolution Protocol
RARP Reverse Address Resolution Protocol
MTU Maximum Transmission Unit

(3) 理想(OSI)

应用数据 应用层
-------------------------------------
安全加密和应用协议 表示层 应用层
-------------------------------------
建立连接(电话) 会话层
----------------------------------------------------
TCP或UDP协议 传输层
-----------------------------------------------------
IP协议 网络层
-----------------------------------------------------
硬件相适应的协议 数据链路层
-----------------------------------------------------
硬件连接 物理层

4. RFC(Request For Comments)征求意见书(搜索)
官网网站: http://www.ietf.org/

[3] 家庭网络如何工作?
1. A电脑和B电脑(虚拟机)如何通信?
A电脑(telnet)-->B电脑(telnetd)

2. A电脑如何和baidu通信?
百度

[4] 资料&工具
1. 资料
《TCP/IP协议详解卷一》

2. 工具
WireShark 局域网内的抓包工具

[5] 网络参数设置
1. ip地址
(1) 作用
网络中唯一标示一台主机

(2) 组成
网络地址 + 主机号
网络地址: 用于区分两台主机是否在同一个网络之内,类似于电话区号
主机号: 用于区分同一个网络内的两台主机

(3) 长度
IPv4 32bit
IPv6 128bit

(4) 字节序(2.字节序目录下)
网络字节序--大端

(5) 表示方法(点分十进制)
将IP地址中的4个字节,分别用10进制表示出来,然后用"."分隔开来的写法
例: 192 .168 .1 .1
11000000 10101000 00000001 00000001

(6) 分类
网络地址长度 主机号长度 高位规定 地址范围 私网(局域网)地址 保留地址
A 1 3 0 0.x.x.x-127.x.x.x 10.x.x.x 127.x.x.x(环回地址)
B 2 2 10 128.x.x.x-191.x.x.x 172.16.x.x-172.32.x.x 169.254.x.x(表示网络中没有DHCP服务器)
C 3 1 110 192.x.x.x-223.x.x.x 192.168.x.x
D 组播 1110
E 保留
注意:网络地址 主机号为0的地址
广播地址 主机号为全1的地址, 例: 192.168.0.255

2. 子网掩码(mask)
IP & mask = 网络地址
IP & ~mask = 主机号

例:
十进制 二进制
IP地址 192.168.0.88 11000000 10101000 00000000 01011000
&子网掩码 255.255.255.0 11111111 11111111 11111111 00000000
----------------------------------------------------------------
192.168.0.0 11000000 10101000 00000000 00000000

思考: IP地址根据范围可以获取地址类型,根据地址类型,可以知道网络地址位数
为什么还需要子网掩码?
有时候需要将C类地址的局域网分成更小的相互独立的局域网,可以将主机地址拿
一部分出来做,网络地址,这时就可以达到,将C类地址组成的局域网分成更小的
局域网的目的,比如:
十进制 二进制
IP地址 192.168.0.88 11000000 10101000 00000000 01011000
&子网掩码 255.255.255.0 11111111 11111111 11111111 11100000
----------------------------------------------------------------
192.168.0.0 11000000 10101000 00000000 01011111

3. 网关
一个网络通向另一个网络的关口
(A电脑<----网关---->baidu)

4. DNS服务器
将域名(计算机名)---->IP地址
www.baidu.com 服务器IP地址

[6] A电脑(telnet)-->B电脑(telnetd)
A电脑网络参数:
网卡地址 00-0C-29-D5-D3-EE
IP地址 192.168.0.159
网关 192.168.0.1
DNS服务器 192.168.0.1

B电脑(虚拟机)网络参数:
网卡地址 00:0c:29:9e:bc:b8
IP地址 192.168.0.249
网关 192.168.0.1
DNS服务器 192.168.0.1

(1) 计算机A: IP(计算机B)---ARP(地址解析协议)--->网卡地址(计算机B的)(MAC地址)
(2) 计算机A: TCP协议包----B电脑的网卡地址--->计算机B
(3) 计算机B: TCP协议包----A电脑的网卡地址--->计算机A
(4) 计算机A: TCP协议包----B电脑的网卡地址--->计算机B
(5) 计算机B: telnet协议包---B电脑的网卡地址--->计算机B

A电脑--->baidu
(1) 计算机A: www.baidu.com---DNS协议包--->IP地址(baidu) 网关是DNS服务器
(2) 计算机A: TCP协议包---网关的网卡地址--->网关---baidu的IP地址--->baidu
(3) baidu: TCP协议包---网关的IP地址--->网关---A电脑的网卡地址--->A电脑
(4) 计算机A: TCP协议包---网关的网卡地址--->网关---baidu的IP地址--->baidu
(5) 计算机A: HTTP协议包---网关的网卡地址--->网关---baidu的IP地址--->baidu

计算机<----网关---->baidu

端口号:
1. 作用
在同一台机器中,唯一标识一个进程,用于接收到数据包时,知道是发给哪个进程的

2. 长度
16bit

3. 分类
1-1023 知名端口号(已经分配给有名的服务程序, telnet(23), ...)
1024-49151 注册端口号(非知名的服务程序,注册的端口号)
49152-65535 动态分配端口号(系统自动分配给应用程序)



[7] 数据包格式
ARP
---------------------------------------
dest mac | src mac | ARP协议数据
---------------------------------------

TCP
---------------------------------------------------
dest mac | src mac | src ip | dest ip | TCP协议数据
---------------------------------------------------

telnet
------------------------------------------------------------------------------
dest mac | src mac | src ip | dest ip | src port | dest port | telnet 协议数据
------------------------------------------------------------------------------

思考: 发送数据给目标机器上的目标程序,至少告诉发送程序,目标程序的哪些信息?
目标机器的IP地址
目标程序的端口号

[8] linux系统网络实现
应用层 data 应用程序
----------------------------------------------------------
socket编程接口(所有操作系统接口完全相同)
-------------------------------------------------
传输层 TCP(UDP) + data
------------------------------------------------- 内核
网络层 IP + TCP(UDP) + data
-------------------------------------------------
数据链路层 ETH + IP + TCP(UDP) + data
----------------------------------------------------------
物理层 硬件

[9] socket
1. 历史
1982 本地通信
1986 网络编程

2. 原理
一种特殊的文件

3. 目的
将通信操作模拟成文件操作

4. 类型及用途
SOCK_STREAM(流式套接字) TCP
SOCK_DGRAM(报文套接字) UDP
SOCK_RAW(原始套接字) IP协议、ICMP协议

[10] ip地址转换

    // 1. 头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// 2. 数据结构
typedef unsigned int in_addr_t;
struct in_addr {
unsigned int s_addr;
};

//定义成结构体的好处:
//可扩展性强
//类型检查更严格
struct in_addr addr;

struct in_addr1 {
unsigned int s_addr;
};

(in_addr1)addr; //语法上是错误的

// 3. 函数
//ip字符串--->32bit整数(网络字节序--大端)
/*
* @param[in]cp ip字符串
* @param[out]inp整数形式的IP地址
* @return @li !0 cp中的IP地址有效
* @li 0cp中的IP地址无效
*/
int inet_aton(const char *cp, struct in_addr *inp);

/*
* @param[in]cp ip字符串
* @return 整数形式的IP地址
* @li INADDR_NONE(-1)cp中的IP地址无效
* @notesbug-1(255.255.255.255是有效的IP地址,不过目前没有用到)
*/
in_addr_t inet_addr(const char *cp);


//32bit整数(网络字节序--大端)--->ip字符串
/*
* @param[in]in 整数形式的IP地址
* @return ip字符串
*/
char *inet_ntoa(struct in_addr in);
[12] 整数的字节序转换

   //  1. 头文件
#include <arpa/inet.h>

// 2. 函数
//主机字节序--->网络字节序
/*
* @param[in]hostlong(hostshort) 主机字节序的整数
* @return 网络字节序的整数
*/
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);

//网络字节序--->主机字节序
/*
* @param[in]hostlong(hostshort) 网络字节序的整数
* @return 主机字节序的整数
*/
uint32_t ntohl(uint32_t hostlong);
uint16_t ntohs(uint16_t hostshort);
[13] 练习
编写一个order程序,完成如下操作:
./order 192.168.0.88 8888
(1) 将IP地址和端口转换成网络字节序打印
(2) 将网络字节序的IP地址和端口转换回IP字符串和主机字节序的端口,打印
#include <stdio.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// ./order 192.168.0.88 8888
int main(int argc, const char *argv[])
{
in_addr_t net_ip;
unsigned short net_port;
struct in_addr addr;

if (argc < 3){
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(EXIT_FAILURE);
}

// 转成网络字节序,打印
net_ip = inet_addr(argv[1]);
net_port = htons(atoi(argv[2]));

printf("net_ip = %08x, net_port = %04x\n", net_ip, net_port);

// 转换回主机字节序, 打印
// addr.s_addr = net_ip;
printf("ip = %s, port = %d\n", inet_ntoa(*((struct in_addr *)(&net_ip))), ntohs(net_port));

return 0;
}


[14] UDP数据通讯原理
UDP数据通讯分服务端(软件)和客户端端:
服务端(软件)(服务器)先运行,服务端,不需要事先知道客户端IP和port
客户端(软件)(客户端机器)后运行,一定是客户端先给服务端发包,客户端一定先知道服务端的IP和port

[15] UDP通信实现

   // 1. 头文件
#include <sys/types.h>
#include <sys/socket.h>

// 2. 数据结构
// Internet协议地址结构
struct sockaddr{
// 地址的通信领域
unsigned short int sa_family;

// ip(4B) 和 port(2B)
char sa_data[14];
};

// 通用数据结构
struct sockaddr_in {
unsigned short int sin_family;
unsigned short int sin_port; // port
struct in_addr sin_addr; // ip地址

// 填充0 (8B)
unsigned char sin_zero[sizeof (struct sockaddr) -
(sizeof (unsigned short int)) -
sizeof (unsigned short int) -
sizeof (struct in_addr)];
};
3. 函数
服务端流程
(1) 创建套接字(创建并且打开套接字)

            /*
* @param[in]domain 通信领域
*@li AF_UNIX, AF_LOCAL unix域套接字通信(本机进程间)
*@li AF_INET IPv4协议通信
*@li AF_INET6 IPv6协议通信
* @param[in]type 套接字类型
*@li SOCK_STREAM 流式套接字
*@li SOCK_DGRAM 报文套接字
*@li SOCK_RAW 网络层的协议访问
* @param[in]protocol 协议标识
*@li 0 使用默认协议
*
* @return 文件描述符
*@li -1 创建失败(错误码见errno)
*/
int socket(int domain, int type, int protocol);
(2) 绑定ip地址和port(到socket(一定一个进程创建))
            /*
* @param[in]sockfd socket
* @param[in]addr 绑定地址(ip地址和port)
* @param[in]addrlen addr的字节数 * @return@li 0 绑定成功
*@li -1 创建失败(错误码见errno)
*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(3) 接收数据包
            /*
* @param[in]sockfd socket
* @param[out] buf 接收数据包的buf
* @param[in]len buf的字节数
* @param[in]flags 0
* @param[out] src_addr 源地址(IP和Port)
*@NULL 不接收源地址,此时addrlen也必须为NULL
* @param[in | out] addrlen(输入) src_addr缓冲区字节数
* addrlen(输出) 实际地址大小
* @return@li >= 0 实际接收的字节数
*@li -1 创建失败(错误码见errno)
*/
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
(4) 发送数据包

            /*
* @param[in]sockfd socket
* @param[out] buf 发送数据包的buf
* @param[in]len 发送数据的字节数
* @param[in]flags 0
* @param[out] dest_addr 目标地址(IP和Port)
* @param[in]addrlen dest_addr字节数
* @return@li >= 0 实际发送的字节数
*@li -1 发送失败(错误码见errno)
*/
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
(5) 关闭socket

int close(int sockfd);

客户端流程
(1) 创建套接字(创建并且打开套接字)
(2) 发送数据包
(3) 接收数据包
(4) 关闭socket

客户端:

/*
* 实现目标:
* udp客户端
*
* 实现步骤:
* 1. 创建socket(创建并打开套接字文件)
* 2. 接收用户输入
* 3. 发送用户输入到服务端
* 4. 接收并显示服务端返回
* 5. 关闭socket
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <strings.h>

// net
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// ./client <server ip> <server port>
int main(int argc, const char *argv[])
{
int sockfd;
int len;
char packet[1024] = {'\0'};
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);

if (argc < 3) {
fprintf(stderr, "Usage: %s <server ip> <server port>\n", argv[0]);
exit(EXIT_FAILURE);
}

// 1. 创建socket(创建并打开套接字文件)
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd) {
perror("Fail to socket.");
exit(EXIT_FAILURE);
}

bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);

while (strcmp(packet, "bye") != 0) {

// 2. 接收用户输入
putchar('\r');
putchar('>');
fgets(packet, sizeof(packet), stdin);
packet[strlen(packet) - 1] = '\0';

// 3. 发送用户输入到服务端
len = sendto(sockfd, packet, strlen(packet), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (len == -1) {
perror("Fail to sendto");
exit(EXIT_FAILURE);
}

// 4. 接收并显示服务端返回
len = recvfrom(sockfd, packet, sizeof(packet), 0, (struct sockaddr *)&server_addr, &addrlen);
if (-1 == len) {
perror("Fail to recvfrom.");
exit(EXIT_FAILURE);
}
packet[len] = '\0';

printf("------------------------------\n");
printf("%s\n", packet);
printf("------------------------------\n");
}

// 5. 关闭socket
close(sockfd);

return 0;
}
服务端:

/*
* 实现目标:
* UDP服务端
*
* 实现步骤:
* 1. 创建socket(创建并打开套接字文件)
* 2. 绑定ip和端口到socket
* 3. 接收数据
* 4. 发送相同数据回客户端
* 5. 关闭socket
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <strings.h>

// net
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// server <ip> <port>
int main(int argc, const char *argv[])
{
int len;
int sockfd;
char packet[1024] = {'\0'};
struct sockaddr_in server_addr;
struct sockaddr_in peer_addr;
socklen_t addrlen = sizeof(struct sockaddr_in);

if (argc < 3) {
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(EXIT_FAILURE);
}

// 1. 创建socket(创建并打开套接字文件)
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd) {
perror("Fail to socket.");
exit(EXIT_FAILURE);
}

// 2. 绑定ip和端口到socket
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);

if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Fail to bind.");
exit(EXIT_FAILURE);
}

while (1) {
// 3. 接收数据
len = recvfrom(sockfd, packet, sizeof(packet), 0, (struct sockaddr *)&peer_addr, &addrlen);
if (-1 == len) {
perror("Fail to recvfrom.");
exit(EXIT_FAILURE);
}
packet[len] = '\0';

printf("------------------------------\n");
printf("ip : %s\n", inet_ntoa(peer_addr.sin_addr));
printf("port : %d\n", ntohs(peer_addr.sin_port));
printf("recv : %s\n", packet);
printf("------------------------------\n");

// 4. 发送相同数据回客户端
len = sendto(sockfd, packet, strlen(packet), 0, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
if (len == -1) {
perror("Fail to sendto");
exit(EXIT_FAILURE);
}
}

// 5. 关闭socket
close(sockfd);

return 0;
}

更多相关文章

  1. linux 内核协议栈
  2. 如果后台的SVN服务器IP地址更改了,如何修改客户端的连接url呢?
  3. Linux 修改ip地址
  4. linux配置虚拟IP地址方法
  5. 使用.NetCore在Linux上写TCP listen 重启后无法绑定地址
  6. Linux系统下用C语言获取MAC地址
  7. 6、linux网络编程--UDP协议编程
  8. Linux协议栈(3)——接收流程及函数
  9. Red Hat Linux下如何修改网卡MAC地址

随机推荐

  1. Android 获取网络时间
  2. 复选框
  3. Android 图片倒影效果源码
  4. android嵌入式底层开发
  5. 使用Android studio分析内存泄露
  6. The android SDK requaires Android Deve
  7. android调用cocos2dx例子
  8. android 设置边框圆角
  9. android IP
  10. android 易忘知识