Linux Tcp/ip UDP基本概念(6.8)

[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 0										cp中的IP地址无效
         */
        int inet_aton(const char *cp, struct in_addr *inp);
        
        /*
         * @param[in]		cp 											ip字符串
         * @return 			整数形式的IP地址
         *            	@li INADDR_NONE(-1)			cp中的IP地址无效
         * @notes				bug											-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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目:使用AngularJs编写的简单 益智游戏(附源代码)  这是一个简单的 javascript 项目。这是一个拼图游戏,也包含一个填字游戏。这个游戏玩起来很棒。有两个不同的版本可以玩这个游戏。你也可以玩填字游戏。 关于游戏 这款游戏的玩法很简单。如上所述,它包含拼图和填字游戏。您可以通过移动图像来玩滑动拼图。您还可以选择要在滑动面板中拥有的列数和网格数。 另一个是填字游戏。在这里你只需要找到浏览器左侧提到的那些单词。 要运行此游戏,您需要在系统上安装浏览器。下载并在代码编辑器中打开此项目。然后有一个 index.html 文件可供您修改。在命令提示符中运行该文件,或者您可以直接运行索引文件。使用 Google Chrome 或 FireFox 可获得更好的用户体验。此外,这是一款多人游戏,双方玩家都是人类。 这个游戏包含很多 JavaScript 验证。这个游戏很有趣,如果你能用一点 CSS 修改它,那就更好了。 总的来说,这个项目使用了很多 javascript 和 javascript 库。如果你可以添加一些具有不同颜色选项的级别,那么你一定可以利用其库来提高你的 javascript 技能。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值