第四章 Linux网络编程

在这里插入图片描述

ARP 协议

在这里插入图片描述
ARP 协议(Address Resolution Protocol)通过 IP 地址查找对应的 MAC 地址。

当一个主机需要发送数据给另一个主机时,它首先会检查本地的 ARP 缓存表(ARP cache)中是否存在目标主机的 MAC 地址。如果存在,则直接使用该 MAC 地址进行数据发送。如果不存在该 MAC 地址,则需要使用 ARP 协议来获取目标主机的 MAC 地址。

主机发送一个 ARP 请求消息(ARP Request)广播到局域网上的所有主机。该请求中包含了源主机的 MAC 地址、IP 地址以及目标主机的 IP 地址。其他主机收到该请求后,会检查自己的 IP 地址是否与目标主机的 IP 地址匹配。如果匹配,则认为是 ARP 请求的目标主机,并向源主机发送 ARP 响应消息(ARP Reply),其中包含了自己的 MAC 地址。源主机接收到 ARP 响应后,将目标主机的 IP 地址和 MAC 地址的映射关系存储在本地的 ARP 缓存表中,并使用目标主机的 MAC 地址进行数据发送。

通过这种方式,ARP 协议实现了通过 IP 地址查找对应的 MAC 地址,从而确保数据包能够正确地发送给目标主机。

字节序(用联合体判断大端小端)

/*  
    字节序:字节在内存中存储的顺序。
    小端字节序:数据的高位字节存储在内存的高位地址,低位字节存储在内存的低位地址
    大端字节序:数据的低位字节存储在内存的高位地址,高位字节存储在内存的低位地址
*/

// 通过代码检测当前主机的字节序
#include <stdio.h>

int main() {

    union {
        short value;    // 2字节
        char bytes[sizeof(short)];  // char[2]
    } test;

    test.value = 0x0102;
    if((test.bytes[0] == 1) && (test.bytes[1] == 2)) {
        printf("大端字节序\n");
    } else if((test.bytes[0] == 2) && (test.bytes[1] == 1)) {
        printf("小端字节序\n");
    } else {
        printf("未知\n");
    }

    return 0;
}

在这里插入图片描述
在这里插入图片描述

字节序转换函数

/*

网络通信时,需要将主机字节序转换成网络字节序(大端),
另外一段获取到数据以后根据情况将网络字节序转换成主机字节序。

// 转换端口
uint16_t htons(uint16_t hostshort);		// 主机字节序 - 网络字节序
uint16_t ntohs(uint16_t netshort);		// 主机字节序 - 网络字节序

// 转IP
uint32_t htonl(uint32_t hostlong);		// 主机字节序 - 网络字节序
uint32_t ntohl(uint32_t netlong);		// 主机字节序 - 网络字节序

*/


#include <stdio.h>
#include <arpa/inet.h>

int main() {

    // htons 转换端口
    unsigned short a = 0x0102;
    printf("a : %x\n", a);
    unsigned short b = htons(a);
    printf("b : %x\n", b);

    printf("=======================\n");

    // htonl  转换IP
    char buf[4] = {192, 168, 1, 100};
    int num = *(int *)buf;
    int sum = htonl(num);
    unsigned char *p = (char *)&sum;

    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));

    printf("=======================\n");

    // ntohl
    unsigned char buf1[4] = {1, 1, 168, 192};
    int num1 = *(int *)buf1;
    int sum1 = ntohl(num1);
    unsigned char *p1 = (unsigned char *)&sum1;
    printf("%d %d %d %d\n", *p1, *(p1+1), *(p1+2), *(p1+3));
    
     // ntohs


    return 0;
}

在这里插入图片描述
在这里插入图片描述

IP地址转换函数

#include <arpa/inet.h>
// p:点分十进制的IP字符串,n:表示network,网络字节序的整数
int inet_pton(int af, const char *src, void *dst);
    af:地址族: AF_INET  AF_INET6
    src:需要转换的点分十进制的IP字符串
    dst:转换后的结果保存在这个里面

// 将网络字节序的整数,转换成点分十进制的IP地址字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
    af:地址族: AF_INET  AF_INET6
    src: 要转换的ip的整数的地址
    dst: 转换成IP地址字符串保存的地方
    size:第三个参数的大小(数组的大小)
    返回值:返回转换后的数据的地址(字符串),和 dst 是一样的
#include <stdio.h>
#include <arpa/inet.h>

int main() {

    // 创建一个ip字符串,点分十进制的IP地址字符串
    char buf[] = "192.168.1.4";
    unsigned int num = 0;

    // 将点分十进制的IP字符串转换成网络字节序的整数
    inet_pton(AF_INET, buf, &num);
    unsigned char * p = (unsigned char *)&num;
    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));


    // 将网络字节序的IP整数转换成点分十进制的IP字符串
    char ip[16] = "";
    const char * str =  inet_ntop(AF_INET, &num, ip, 16);
    printf("str : %s\n", str);
    printf("ip : %s\n", str);
    printf("%d\n", ip == str);

    return 0;
}

在这里插入图片描述

TCP通信实现(服务器端)

// TCP 通信的服务器端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建socket(用于监听的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "192.168.193.128", saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.监听
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.接收客户端连接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    // 输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);

    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        
        // 获取客户端的数据
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客户端断开连接
            printf("clinet closed...");
            break;
        }

        char * data = "hello,i am server";
        // 给客户端发送数据
        write(cfd, data, strlen(data));
    }
   
    // 关闭文件描述符
    close(cfd);
    close(lfd);

    return 0;
}

TCP通信实现(客户端) client.c

// TCP通信的客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.创建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.193.128", &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    if(ret == -1) {
        perror("connect");
        exit(-1);
    }
    
    // 3. 通信
    char recvBuf[1024];
    int i = 0;
    while(1) {
        
        sprintf(recvBuf, "data : %d\n", i++);
        
        // 给服务器端发送数据
        write(fd, recvBuf, strlen(recvBuf)+1);

        int len = read(fd, recvBuf, sizeof(recvBuf));
        if(len == -1) {
            perror("read");
            exit(-1);
        } else if(len > 0) {
            printf("recv server : %s\n", recvBuf);
        } else if(len == 0) {
            // 表示服务器端断开连接
            printf("server closed...");
            break;
        }

        sleep(1);
    }

    // 关闭连接
    close(fd);

    return 0;
}

server_process.c

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>

void recyleChild(int arg) {
    while(1) {
        int ret = waitpid(-1, NULL, WNOHANG);
        if(ret == -1) {
            // 所有的子进程都回收了
            break;
        }else if(ret == 0) {
            // 还有子进程活着
            break;
        } else if(ret > 0){
            // 被回收了
            printf("子进程 %d 被回收了\n", ret);
        }
    }
}

int main() {

    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = recyleChild;
    // 注册信号捕捉
    sigaction(SIGCHLD, &act, NULL);
    

    // 创建socket
    int lfd = socket(PF_INET, SOCK_STREAM, 0);
    if(lfd == -1){
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 绑定
    int ret = bind(lfd,(struct sockaddr *)&saddr, sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 监听
    ret = listen(lfd, 128);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 不断循环等待客户端连接
    while(1) {

        struct sockaddr_in cliaddr;
        int len = sizeof(cliaddr);
        // 接受连接
        int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
        if(cfd == -1) {
            if(errno == EINTR) {
                continue;
            }
            perror("accept");
            exit(-1);
        }

        // 每一个连接进来,创建一个子进程跟客户端通信
        pid_t pid = fork();
        if(pid == 0) {
            // 子进程
            // 获取客户端的信息
            char cliIp[16];
            inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
            unsigned short cliPort = ntohs(cliaddr.sin_port);
            printf("client ip is : %s, prot is %d\n", cliIp, cliPort);

            // 接收客户端发来的数据
            char recvBuf[1024];
            while(1) {
                int len = read(cfd, &recvBuf, sizeof(recvBuf));

                if(len == -1) {
                    perror("read");
                    exit(-1);
                }else if(len > 0) {
                    printf("recv client : %s\n", recvBuf);
                } else if(len == 0) {
                    printf("client closed....\n");
                    break;
                }
                write(cfd, recvBuf, strlen(recvBuf) + 1);
            }
            close(cfd);
            exit(0);    // 退出当前子进程
        }

    }
    close(lfd);
    return 0;
}
#include <iostream>
#include <fstream>
#include <string>
#include <list>
using namespace std;

// 学生类
class Student {
private:
    string name;
    int regID;
    int score;

public:
    Student(string name, int regID, int score) {
        this->name = name;
        this->regID = regID;
        this->score = score;
    }

    string getName() const {
        return name;
    }

    int getRegID() const {
        return regID;
    }

    int getScore() const {
        return score;
    }
};

// HashMap类
class HashMap {
private:
    static const int SIZE = 10;
    list<Student*> hashTable[SIZE];

    int hashCode(int regID) {
        return regID % SIZE;
    }

public:
    Student* get(int regID) {
        int index = hashCode(regID);
        for (Student* student : hashTable[index]) {
            if (student->getRegID() == regID) {
                return student;
            }
        }
        return nullptr;
    }

    bool put(Student* student) {
        int regID = student->getRegID();
        int index = hashCode(regID);
        for (Student* s : hashTable[index]) {
            if (s->getRegID() == regID) {
                return false; // 已存在相同学号的学生
            }
        }
        hashTable[index].push_back(student);
        return true;
    }
};

int main() {
    HashMap studentMap;

    // 读取数据文件
    ifstream file("data.txt");
    if (file) {
        string line;
        while (getline(file, line)) {
            size_t pos1 = line.find(',');
            string name = line.substr(0, pos1);

            size_t pos2 = line.find(',', pos1 + 1);
            int regID = stoi(line.substr(pos1 + 1, pos2 - pos1 - 1));

            int score = stoi(line.substr(pos2 + 1));
            
            // 创建Student对象并添加到HashMap中
            Student* student = new Student(name, regID, score);
            studentMap.put(student);
        }
        file.close();

        // 输入学号,查询学生信息
        int regID;
        cout << "请输入学号:";
        cin >> regID;
        
        Student* student = studentMap.get(regID);
        if (student) {
            cout << "姓名:" << student->getName() << endl;
            cout << "学号:" << student->getRegID() << endl;
            cout << "成绩:" << student->getScore() << endl;
        } else {
            cout << "未找到该学号对应的学生信息" << endl;
        }
    } else {
        cout << "无法打开数据文件" << endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值