网络编程笔记

计算机网络的构成

计算机网络是由硬件、软件、协议(一系列规则和约定的集合)共同组成

细分可以分解为:

终端设备:

指的是计算机网络的用户端(客户端),包括个人计算机,笔记本电脑,手机等,这些设备通过网络连接到其他设备,并使用各种应用程序进行通信,资源共享和数据处理

服务器:

是网络上提供服务的主要设备。如网页服务器,文件服务器,数据库服务器等,由他们存储和管理资源,并相应终端设备的请求,提供所需的服务和数据

交换设备:

简单来讲指的是路由器或者交换机,用于在网络中转发数据包,交换机是在局域网内交换数据包,而路由器是在不同网络之间转发数据包,确保能沿着正确的路径到达目标

传输媒体:

传输媒体指的是数据在网络传输中的物理途径,包括导线(铜丝),光纤,无线电波等,不同的传输媒体具有不同的传输速度和传输距离

协议:

协议是设备之间通信的规则和约定,网络使用多种协议来控制数据包的形式,传输方式,错误检测和纠正等细节,常见的网络协议包括TCP/IP、HTTP,SMTP等

分层模型

分层是一种将复杂系统,结构或设计分为不同层次或级别的方法,每个层次都有特定的功能和职责,各层次之间通过自定义接口相互交互,形成一个有序的整体

分层设计的主要优势:

模块化:

分层设计将系统划分为相对独立的模块或层次,使得开发、维护和测试变得更加可管理和灵活

隔离性:

每个层次的功能是相对独立的,这意味着在更改一个层次时不会影响到其他层次,这种隔离性有助于降低风险,简化了系统的维护

标准化:

分层设计通常要求定义清晰的接口和协议,这使得不同层次之间可以交流合作,标准化有助于实现互操作性和组件的可替换性

可扩展性:

分层设计使得系统可以按需进行扩展,新的功能可以添加到特定层次而不必影响其他部分

理解和教育:

分层设计使得系统的结构和功能变得更加清晰和易于理解,它可以帮助新的开发人员更快的熟悉系统,一边更好理解各个组成部分之间的关系

OSI七层模型

国际标准化组织(ISO)发布的用于描述计算机网络通信过程的标准化体系结构

称为OSI七层模型

OSI七层模型详述:

(接收过程:物理层>>应用层)(发送过程:应用层>>物理层)

物理层:

此为网络的最底层,涉及到物理介质和电信号的传输,负责将比特流(0和1)传输到网络媒体上,如电缆,光纤等。物理层处理电压,电流,频率等电气特性,以及连接器,接口和传输速率等细节

数据链路层:

数据链路层负责在直接相连的节点之间传输数据,它将比特流组织成帧,处理帧的开始和结束,还负责错误的检测和处理(处理方式为丢弃原包,请求重新发送新包),以确保数据的可靠传输

tip:当出现较大文件传输时,数据链路层会将文件数据封装成帧(数据块前增加一个标志位,数据块后增加一个标志位使将其包裹起来形成帧),分成多块,后再数据后增加一个标志位负责检查错误,假设传输的内容是1010,就在后面增加检验标志位2(1+0+1+0)如果在传输后发现内容加和不等于2则代表这段数据出现错误,将此段文件数据丢弃并且请求重新发送数据包

网络层:

网络层负责数据的路由和转发以及在多个网络之间的数据传输,它通过逻辑寻址(如IP地址)将数据从源节点传输到目标节点,处理分组传输和路由选择,确保数据在网络中能正确到达

传输层:

传输层提供端到端的数据传输服务,确保数据的可靠传输和有序交付。它处理数据分割,流量控制,拥塞控制和错误恢复,以确保数据在网络中的高效传输

会话层:

会话层负责建立、管理和终止会话,即通信双方间的逻辑连接,它处理会话控制、同步、数据交换和故障恢复,以确保通信的顺利进行

表示层:

表示层处理数据的格式转换、加密、压缩和解压缩,以确保在不同系统之间的数据交换时能够正确解释和处理数据,它还负责数据的语法和语义转换

应用层:

应用层是最高层,直接面向用户的应用程序,它为用户提供了网络服务,如电子邮件,文件传输,远程访问等,应用层协议定义了应用程序和网络之间的接口

TCP / IP 四层模型

TCP / IP 将OSI模型简化为四层,分别为

应用层(应用层、会话层,表示层)

传输层

网络层

通信链路层(物理层、数据链路层)

IP地址与端口号

IP协议工作在网络层   TCP协议工作在传输层

网络层通过逻辑寻址(IP地址)将数据从源节点传输到目标节点,处理分组传输和路由选择,确保数据在网络中正确到达

端口号

在传输后期,假如数据通过逻辑寻址(IP地址)找到了唯一标识的主机,但是如果这台主机上同时安装了QQ或者微信两款或者多款接收信号,那么该数据如何区分自己应该表示在哪个APP上?

IP地址可以在整个网络中唯一标识一台主机,端口号可以在一台主机上唯一标识一个进程,这样我们就可以用“IP地址+端口号”在网络中唯一标识一个进程,有了唯一标识,也就有了被找到的可能

IPv4地址是一个4字节(32bit)的整数,端口号是一个2字节(16bit)的整数

IP地址的分类

IP地址分类为五种类型,分别为A类、B类、C类、D类、E类

A类:0开头+7位网络号  +  24(8+8+8)位主机号  网络号 : 主机号 = 8 : 24

B类:10开头+14位网络号 + 16(8+8)位主机号 网络号 : 主机号 = 16 : 16

C类:110开头+21位网络号 + 8位主机号 网络号 : 主机号 = 24 : 8

D类:1110开头 组播地址

E类:1111开头 留待后用

A类最大主机数:2^24   16777214

B类最大主机数:2^16    65534

C类最大主机数:2^8      254

无分类地址CIDR

子网划分:

未做子网划分的IP地址:网络地址+主机地址

做子网划分后的IP地址:网络地址+子网网络地址+子网主机地址

习题解析

1 通过子网数来算子网掩码

需将B类IP地址167.194.0.0划分成28个子网:

B类IP地址中的结构构成为16网络号+16主机号

可以将此IP地址子网掩码拆分为255.255.00000000.00000000

划分为28个子网,但是没有正好等于28的二进制次方数,所以要找最接近的

2^5 = 36 > 28

所以要在子网掩码主机号中改变前五个0为1也就是

255.255.11111000.00000000 = 255.255.248.0

2 通过主机数来计算子网掩码

将B类IP地址167.194.0.0划分成若干个子网,每个子网内有主机500台

B类子网掩码缺省:255.255.0.0

每个子网有主机500台(2^9 = 512)

B类主机号有16位,所以16 - 9 = 7

主机号前七位应为1

所以子网掩码可以表示为255.255.11111110.0 = 255.255.254.0

3 利用子网掩码计算中最大有效子网数

一个A类IP地址,子网掩码为255.254.0.0,它所能划分的最大有效子网数是多少

A类IP地址的子网掩码缺省为:255.0.0.0

题目中给定子网掩码为255.224.0.0,将主机号部分化为二进制为

11111111.11100000.00000000.00000000,子网号中有3个1,所以最大有效子网数为2^3=8个

4 利用子网掩码计算最大可用主机数

A类IP地址,子网掩码为255.252.0.0,将它划分成若干个子网络,每个子网络中可用主机数有多少

A类IP地址的子网掩码缺省为255.0.0.0

题中给出的子网掩码可以写为11111111.11111100.00000000.00000000

其中主机位有18个0,所以每个子网络中可用主机数为2^18 - 2 = 262142个

(注意去掉网段地址和广播地址)

5 利用子网掩码确定子网络的起止位置

B类IP地址172.16.0.0,子网掩码为255.255.192.0,它能划分的子网络起止地址为多少

B类IP地址子网掩码缺省为:255.255.0.0

题中所给出的子网掩码可以写为11111111.11111111.11000000.00000000

由于子网掩码主机位中有两个1,所以可以知道一共可以分配四种子网络

四种子网络一共可以容纳2^14 = 16384个主机(主机位有14个1的时候最大数量)

每个子网可以分配16384 / 255 = 64台主机

所以网络起止可以分为:

a 172.16.0.0 - 172.16.63.255

b 172.16.64.0 - 170.16.127.255

c 172.16.128.0 - 170.16.191.255

d 172.16.192.0 - 170.16.255.255

6

需将A类IP地址67.0.0.0划分成245个子网,求子网掩码

A类地址子网掩码缺省为255.0.0.0

需要划分为245个子网,由于2^8 = 256,所以子网号中要存储8个1

也就是11111111.11111111.00000000.00000000 = 255.255.0.0

7

需将A类IP地址67.0.0.0划分成若干个子网,每个子网内有主机30000台,求子网掩码

A类子网掩码缺省为255.0.0.0 = 11111111.00000000.00000000.00000000

需要使每个子网内有主机30000台

由于2^15 = 32768 > 30000

也就是在主机号中最后15位全部置0,则前24 - 15 = 9位为1

也就是11111111.11111111.10000000.00000000 = 255.255.128.0

8

A类IP地址,子网掩码为255.254.0.0,它所能划分的最大有效子网数是多少

由于是A类地址,子网掩码缺省为255.0.0.0 = 11111111.00000000.00000000.00000000

题目中给的子网掩码为255.254.0.0可以写为11111111.11111110.00000000.00000000

则最大有效子网数为2^7 = 128个

9

A类IP地址,子网掩码为255.224.0.0,将它划分成若干子网络,每个子网络中可用主机数为多少

由于是A类IP地址,子网掩码缺省为255.0.0.0

题目中给的子网掩码为255.224.0.0可以写成11111111.11100000.00000000.00000000

可用主机数 = 2^21 - 2个

------------------------------------------单线程非阻塞--------------------------------------------------------------------

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>

#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define CLIEN_MAX 1024

int main(int argc, char* argv[]){
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1){
        perror("socket error");
        exit(1);
    }
    struct sockaddr_in serveraddr, clientaddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(SERVER_PORT);
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(lfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
    if(ret == -1){
        perror("bind error");
        exit(1);
    }
    ret = listen(lfd, 64);
    if(ret == -1){
        perror("listen error");
        exit(1);
    }
    socklen_t addr_len = sizeof(clientaddr);
    int cfd = -1;
    char buf[BUFFER_SIZE];
    int read_ret = 0;
    int cfds[CLIEN_MAX];
    char clientip[32];
    for(int i = 0; i < CLIEN_MAX; i++){
        cfds[i] = -1;
    }

    int flags = fcntl(lfd, F_GETFL);
    flags |= O_NONBLOCK;
    fcntl(lfd, F_SETFL, flags);

    while(1){
        cfd = accept(lfd, (struct sockaddr*)&clientaddr, &addr_len);
        if(cfd == -1){
            if(errno == EAGAIN){
            }
            else{
                perror("accept error");
                exit(1);
            }
        }
        else if(cfd > 0){
            printf("鏈夋柊鐨勫鎴风寤虹珛杩炴帴: IP = %s, PORT = %d\n", inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientip, sizeof(clientip)), ntohs(clientaddr.sin_port));
            int flags = fcntl(cfd, F_GETFL);
            flags |= O_NONBLOCK;
            fcntl(cfd, F_SETFL, flags);

            for(int i = 0; i < CLIEN_MAX; i++){
                if(cfds[i] == -1){
                    cfds[i] = cfd;
                    break;
                }
            }
        }
        for(int i = 0; i < CLIEN_MAX; i++){
            if(cfds[i] < 0){
                continue;
            }
            read_ret = read(cfds[i], buf, BUFFER_SIZE);
            if(read_ret < 0){
                if(errno == EAGAIN){
                    continue;
                }
                perror("read error");
                exit(1);
            }
            else if(read_ret == 0){
                printf("瀹㈡埛绔柇寮€杩炴帴 cfd = %d\n", cfds[i]);
                close(cfds[i]);
                cfds[i] = -1;
                continue;
            }
            else{
                write(STDOUT_FILENO, buf, read_ret);
                write(cfds[i], buf, read_ret);
            }
        }
    }
    return 0;
}

select I/O复用

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>

#define SERVER_PORT 8000
#define BUFFER_SIZE 1024

int main(int argc, char* argv[]){
    int lfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字
    if(lfd == -1){
        perror("socket error");
        exit(1);
    }
    
    struct sockaddr_in serveraddr,clientaddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(SERVER_PORT);
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    //绑定套接字
    int ret = bind(lfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    if(ret == -1){
        perror("bind error");
        exit(1);
    }
    //将服务器从主动变为被动
    ret = listen(lfd,64);
    if(ret == -1){
        perror("listen error");
        exit(1);
    }
    socklen_t addr_len = sizeof(clientaddr);
    int cfd = -1;
    char buf[BUFFER_SIZE];
    int read_ret = 0;
    char clientip[32];
    fd_set all_set,read_set;
    //初始化读位图
    FD_ZERO(&all_set);
    //将lfd添加进all_set集合
    FD_SET(lfd,&all_set);
    int nfds = lfd + 1;
    int select_ret = 0;
    struct timeval timeout;
    //timeout.tv_sec = 5;
    //timeout.tv_usec = 50000;
    while(1){
        read_set = all_set;
        select_ret = select(nfds,&read_set,NULL,NULL,&timeout);
        if(select_ret < 0){
            perror("select error");
            exit(1);
        }
        else if(select_ret == 0){
            //printf("超时时间已到,没有事件触发\n");
            //sleep(2);
            //continue;
        }
        else{
            //printf("timeout.tv_set = %lu, timeout.tv_usec = %lu\n",timeout.tv_sec,timeout.tv_usec);
            if(FD_ISSET(lfd,&read_set)){
                cfd = accept(lfd,(struct sockaddr*)&clientaddr,&addr_len);
                if(cfd == -1){
                    perror("accept error");
                    exit(1);
                }
                else if(cfd > 0){
                    printf("有新的客户端建立连接:IP=%s, PORT=%d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientip,sizeof(clientip)),ntohs(clientaddr.sin_port));
                    FD_SET(cfd,&all_set);
                    if(cfd > (nfds - 1)){
                        nfds = cfd + 1;
                    }
                    if(--select_ret == 0){
                        continue;
                    }
                }
            }
            for(int i = lfd + 1;i < nfds;i++){
                if(FD_ISSET(i,&read_set)){
                    read_ret = read(i,buf,BUFFER_SIZE);
                    if(read_ret < 0){
                        perror("read error");
                        exit(1);
                    }
                    else if(read_ret == 0){
                        printf("客户端断开连接\n");
                        FD_CLR(i,&all_set);
                        close(i);
                        continue;
                    }
                    else{
                        write(STDOUT_FILENO,buf,read_ret);
                        write(i,buf,read_ret);
                    }
                    if(--select_ret == 0){
                        break;
                    }
                }
            }
        }
    }
}
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/epoll.h>

#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define EVENT_SIZE 1024

int main(int argc, char* argv[]){
    int lfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字
    if(lfd == -1){
        perror("socket error");
        exit(1);
    }
    
    struct sockaddr_in serveraddr,clientaddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(SERVER_PORT);
    serveraddr.sin_addr.s_addr = INADDR_ANY;

    int ret = bind(lfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    if(ret == -1){
        perror("bind error");
        exit(1);
    }

    ret = listen(lfd,64);
    if(ret == -1){
        perror("listen error");
        exit(1);
    }
    socklen_t addr_len = sizeof(clientaddr);
    int cfd = -1;
    char buf[BUFFER_SIZE];
    int read_ret = 0;
    char clientip[32];
    int epoll_ret = 0;
    int epfd = epoll_create(64); //创建epoll对象
    struct epoll_event event;    //创建结构体对象
    struct epoll_event events[EVENT_SIZE];  //创建结构体数组
    event.events = EPOLLIN;  //设置监听事件为读事件
    event.data.fd = lfd;     //设置监听文件描述符
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&event);  //将lfd增加到epoll对象里
    while(1){
        epoll_ret = epoll_wait(epfd,events,EVENT_SIZE,0);
        if(epoll_ret < 0){
            perror("epoll error");
            exit(1);
        }
        else if(epoll_ret == 0){

        }
        else{
            for(int i = 0; i < epoll_ret;i++){
                int fd = events[i].data.fd;
                if(events[i].events & EPOLLIN){
                    if(fd == lfd){ //判断是否是通信套接字
                        cfd = accept(lfd,(struct sockaddr*)&clientaddr,&addr_len);
                        printf("有新的客户端建立连接:IP=%s, PORT=%d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientip,sizeof(clientip)),ntohs(clientaddr.sin_port));
                        if(cfd < 0){
                            perror("accept error");
                            exit(1);
                        }
                        else{
                            event.events = EPOLLIN;
                            event.data.fd = cfd;
                            epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&event);
                        }
                    }
                    else{ //客户端套接字
                        read_ret = read(fd,buf,BUFFER_SIZE);
                        if(read_ret < 0){
                            perror("read error");
                            exit(1);
                        }
                        else if(read_ret == 0){
                            printf("客户端断开连接\n");
                            epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
                            close(fd);
                        }
                        else{
                            write(STDOUT_FILENO,buf,read_ret);
                            write(fd,buf,read_ret);
                        }
                    }
                }
            }
        }
    }
}

-------------------------------------------------需  看---------------------------------------------------------------------

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值