学习笔记11(第十三章)

一、知识点归纳

(一)知识点内容

教材学习内容总结

P/IP协议

TCP/IP 是互联网的基础。TCP代表传输控制协议。IP代表互联网协议。目前有两个版本的IP,即IPv4和IPv6。IPv4使用32位地址,IPv6则使用128位地址。

本节围绕IPv4进行讨论,它仍然是目前使用最多的IP版本。

TCP/IP的组织结构分为几个层级,通常称为TCP/IP堆栈。

顶层是使用TCP/IP的应用程序。

用于登录到远程主机的ssh、用于交换电子邮件的邮件、用于Web页面的http等应用程序需要可靠的数据传输。通常,这类应用程序在传输层使用TCP。

另一方面有些应用程序,例如用于查询其他主机的ping命令,则不需要可靠性。这类应用程序可以在传输层使用UDP来提高效率。

主机:主机是支持TCP/IP协议的计算机或设备。每个主机由一个32位的IP地址来标识。

路由器是接收和转发数据包的特殊IP主机。如果有的话, 一个IP数据包可能会经过许多路由器,或者跳跃到达某个目的地。

UDP

UDP是用户数据报协议。

TCP

TCP(传输控制协议)是一种面向连接的协议。

TCP/IP 协议

TCP/IP 传输协议

TCP/IP,即传输控制协议/互联网协议,是网络使用中的基本通信协议。它为互联网中的通信提供了标准和方法,确保网络数据信息的及时、完整传输。从结构上讲,TCP/IP 是一个四层体系结构,包括应用层、传输层、网络层和数据链路层。

主要协议
  • 应用层:包括如 Telnet、FTP、SMTP 等协议,用于接收来自传输层的数据或按照不同应用需求传输数据。
  • 传输层:协议如 UDP、TCP,为平台内的数据传输和共享提供通道。
  • 网络层:协议如 ICMP、IP、IGMP,主要负责网络中数据包的传送。
  • 网络访问层:协议如 ARP、RARP,关注链路管理、错误检测以及通信媒介的处理。

IP 主机和 IP 地址

主机
  • 支持 TCP/IP 协议的计算机或设备。
  • 通过 32 位 IP 地址标识,通常以点分十进制表示,例如:134.121.64.1
IP 地址
  • 包括 NetworkID 和 HostID 字段。
  • 分为 A 至 E 类。例如,B 类 IP 地址包含 16 位 NetworkID 和 16 位 HostID。

IP 协议

  • 在 IP 主机之间发送/接收数据包。
  • 基于“尽最大努力”原则,不保证数据包的顺序或确切送达。

IP 数据包格式

  • 包括 IP 头、发送方地址、接收方 IP 地址和数据。

套接字 API

在网络编程中,TCP/IP 的用户界面通过一系列 C 语言库函数和系统调用实现,统称为套接字 API。

1.套接字地址

struct sockaddr_in {
    sa_family_t sin_family; // AF_INET for TCP/IP
    in_port_t sin_port;     // 端口号
    struct in_addr sin_addr; // IP 地址
};

struct in_addr { // 互联网地址
    uint32_t s_addr; // 网络字节顺序的 IP 地址
};
  • TCP/IP网络的sin_family始终设置为AF_INET
  • sin_port包含端口号,按网络字节顺序排列。
  • sin_addr是按网络字节顺序排列的主机IP地址。

2. 套接字API

(1) int socket(int domain, int type, int protocol)
(2) int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen)
(3) UDP套接字
  • 使用sendto()/recvfrom()来发送/接收数据报。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
(4)TCP套接字

在创建套接字并将其绑定到服务器地址之后,TCP服务器使用listen()和accept()来接收来自客户机的连接

int listen(int sockfdint backlog);
int accept(int sockfd,struct sockaddr *addr,socklen t *addrlen);

(二)苏格拉底挑战

1.知识点一:udpsever



2.知识点二:tcpsever



二、问题与解决

(一)问题

原始套接字的作用是什么?

(二)解决

原始套接字(Raw Sockets)在TCP/IP协议栈中有着特殊的作用。它们通常用于以下几个方面:

直接处理IP层数据包:原始套接字允许应用程序直接发送或接收在IP层的数据包。这意味着应用可以绕过传输层(如TCP或UDP),直接与IP层交互。

自定义协议实现:开发者可以利用原始套接字实现自定义的网络协议或对现有协议进行修改和扩展。例如,可以用于实验性的网络协议开发。

网络诊断和监测工具:原始套接字广泛用于网络监测和诊断工具中,如网络抓包工具(例如Wireshark)和网络测试工具(如ping和traceroute)。这些工具通过原始套接字捕获和分析经过网络接口的所有数据包。

安全应用:在网络安全领域,原始套接字可以用来实现包嗅探器、端口扫描器等安全工具。它们可以帮助安全专家监测网络异常或进行安全评估。

协议分析和调试:原始套接字可以用于深入分析特定协议的行为,帮助开发者理解和调试网络协议的实现。

总之,原始套接字提供了一种在应用层直接访问和操作网络层数据的能力。这虽然带来了灵活性和强大的控制能力,但也可能引起安全问题,因为不当使用可能导致网络安全风险。因此,操作系统通常限制只有特权用户(如root用户)才能创建和使用原始套接字。

三、实践过程与代码

(一)实践


(二)代码

1、udpserver

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define SERVER_PORT 9999
#define BUFF_LEN 1024

void handle_udp_msg(int fd)
{
    char buf[BUFF_LEN];  //接收缓冲区,1024字节
    socklen_t len;
    int count;
    struct sockaddr_in clent_addr;  //clent_addr用于记录发送方的地址信息
    while(1)
    {
        memset(buf, 0, BUFF_LEN);
        len = sizeof(clent_addr);
        count = recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, &len);  //recvfrom是拥塞函数,没有数据就一直拥塞
        if(count == -1)
        {
            printf("recieve data fail!\n");
            return;
        }
        printf("client:%s\n",buf);  //打印client发过来的信息
        memset(buf, 0, BUFF_LEN);
        sprintf(buf, "I have recieved %d bytes data!\n", count);  //回复client
        printf("server:%s\n",buf);  //打印自己发送的信息给
        sendto(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, len);  //发送信息给client,注意使用了clent_addr结构体指针

    }
}


/*
    server:
            socket-->bind-->recvfrom-->sendto-->close
*/

int main(int argc, char* argv[])
{
    int server_fd, ret;
    struct sockaddr_in ser_addr;

    server_fd = socket(AF_INET, SOCK_DGRAM, 0); //AF_INET:IPV4;SOCK_DGRAM:UDP
    if(server_fd < 0)
    {
        printf("create socket fail!\n");
        return -1;
    }

    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址,需要进行网络序转换,INADDR_ANY:本地地址
    ser_addr.sin_port = htons(SERVER_PORT);  //端口号,需要网络序转换

    ret = bind(server_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    if(ret < 0)
    {
        printf("socket bind fail!\n");
        return -1;
    }

    handle_udp_msg(server_fd);   //处理接收到的数据

    close(server_fd);
    return 0;
}


2、client

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

#define SERVER_PORT 9999
#define BUFF_LEN 1024

void udp_client_send(const char *address, const char *message) {
    int client_fd;
    struct sockaddr_in server_addr;
    char buf[BUFF_LEN];

    // 创建UDP socket
    client_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (client_fd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = inet_addr(address);

    // 发送消息给服务器
    sendto(client_fd, message, strlen(message), 0, (const struct sockaddr *) &server_addr, sizeof(server_addr));
    printf("Message sent to server: %s\n", message);

    // 接收服务器的响应
    socklen_t addr_len = sizeof(server_addr);
    int len = recvfrom(client_fd, buf, BUFF_LEN, 0, (struct sockaddr *) &server_addr, &addr_len);
    buf[len] = '\0'; // 确保字符串以null结束
    printf("Response from server: %s\n", buf);

    // 关闭socket
    close(client_fd);
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("Usage: %s <server_ip> <message>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    udp_client_send(argv[1], argv[2]);

    return 0;
}
posted @ 2023-11-11 17:13  6666666mjz  阅读(61)  评论(0)    收藏  举报