如何在C语言中实现Ping功能?

在 Linux 系统中,可以使用 ping 命令来测试网络连通性。要检查与 IP 地址为 192.168.1.1 的主机之间的连接,可以在终端中输入:,,“bash,ping 192.168.1.1,“,,这将发送 ICMP 回显请求到指定的 IP 地址,并显示响应时间及丢包情况。

在Linux系统中,ping命令是一种常用的网络工具,用于测试主机之间的连通性以及测量数据包的往返时间,以下是关于如何在Linux上实现ping命令的详细步骤和解释:

如何在C语言中实现Ping功能?

一、理解ICMP协议

ping命令基于ICMP(Internet Control Message Protocol)协议工作,ICMP允许主机或路由器报告差错情况或提供有关异常情况的报告。ping命令使用的是ICMP回送请求和回送应答报文类型,以判断目标主机是否可达。

二、创建原始套接字

在Linux中,要发送ICMP包,需要使用原始套接字(raw socket),原始套接字可以直接访问网络层,允许我们构造自定义的网络协议数据包。

int ip_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (ip_fd < 0) {
    perror("socket error");
    exit(1);
}

三、设置ICMP包头

ICMP包头包括类型、代码、校验和、标识符和序列号等字段,对于ICMP回送请求,类型字段应设置为8。

struct icmphdr {
    u_int8_t icmp_type;    /* ICMP message type */
    u_int8_t icmp_code;    /* Optional ICMP code */
    u_int16_t icmp_cksum;  /* Optional ICMP checksum */
    union {
        u_int32_t ih_idseq;     /* ICMP ID and sequence number */
        u_int32_t ih_gateway;    /* Target gateway address */
        struct {
            u_int16_t unused;
            u_int16_t mtu;
        } ih_pmtu;
    } icmp_hun;
};

四、计算校验和

由于IP协议是不可靠的传输协议,为了确保数据在传输过程中没有发生变化,需要在ICMP包头中加入校验和字段,校验和的计算方法是将ICMP包头视为由若干个16位整数组成,对这些整数进行二进制反码求和,然后取反码得到校验和。

如何在C语言中实现Ping功能?

unsigned short cal_chksum(void *buffer, int len) {
    unsigned long sum = 0;
    unsigned short *data = buffer;
    while (len > 1) {
        sum += *data++;
        if (sum & 0x80000000)
            sum = (sum & 0xFFFF) + (sum >> 16);
        len -= 2;
    }
    if (len)
        sum += *(unsigned char *)data;
    while (sum >> 16)
        sum = (sum & 0xFFFF) + (sum >> 16);
    return ~sum;
}

五、构造并发送ICMP包

构造ICMP回送请求包,并使用原始套接字发送出去,需要记录当前时间,以便后续计算往返时间。

struct timeval begin_time;
gettimeofday(&begin_time, NULL);
struct icmphdr icmphdr;
icmphdr.icmp_type = ICMP_ECHO;
icmphdr.icmp_code = 0;
icmphdr.icmp_cksum = 0;
icmphdr.icmp_seq = htons(++sequence);
icmphdr.icmp_id = getpid() & 0xFFFF; // Use process ID as the ID field
icmphdr.icmp_cksum = cal_chksum(&icmphdr, sizeof(icmphdr)); // Update checksum after setting other fields
sendto(ip_fd, &icmphdr, sizeof(icmphdr), 0, (struct sockaddr*)&send_addr, sizeof(send_addr));

六、接收ICMP回显应答包并处理

接收到ICMP回显应答包后,解析包头信息,并计算往返时间,如果收到的是其他类型的ICMP包,则忽略。

struct sockaddr_in recv_addr;
socklen_t addrlen = sizeof(recv_addr);
char buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
if (recvfrom(ip_fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&recv_addr, &addrlen) <= 0) {
    perror("recvfrom error");
    exit(1);
}
struct iphdr *iphdr = buffer;
struct icmphdr *icmphdr = (struct icmphdr*)(buffer + (iphdr->ip_hl << 2)); // IP header length is in bytes, shift left by 2 to get it in 32-bit words
if (icmphdr->icmp_type == ICMP_ECHOREPLY && icmphdr->icmp_id == getpid() & 0xFFFF) {
    struct timeval recv_time;
    gettimeofday(&recv_time, NULL);
    int rtt = (recv_time.tv_sec begin_time.tv_sec) * 1000 + (recv_time.tv_usec begin_time.tv_usec) / 1000.0; // Calculate round-trip time in milliseconds
    printf("From: %s, RTT: %d ms
", inet_ntoa(recv_addr.sin_addr), rtt);
} else {
    printf("Received non-echo reply or invalid packet
");
}

七、主函数逻辑

主函数负责解析命令行参数,设置信号处理函数以定时发送ICMP包,并根据用户输入的目标地址构造并发送ICMP包。

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <hostname/IP>
", argv[0]);
        exit(1);
    }
    // Create raw socket for ICMP
    int ip_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (ip_fd < 0) {
        perror("socket error");
        exit(1);
    }
    // Set up signal handler for alarm to send ICMP packets at regular intervals
    struct sigaction act;
    act.sa_handler = handle_alarm;
    act.sa_flags = SA_SIGINFO; // Use SA_SIGINFO to pass additional data to signal handler
    sigemptyset(&act.sa_mask);
    sigaction(SIGALRM, &act, NULL);
    // Set up destination address based on command line argument
    struct sockaddr_in send_addr;
    memset(&send_addr, 0, sizeof(send_addr));
    send_addr.sin_family = AF_INET;
    if (inet_aton(argv[1], &send_addr.sin_addr) == 0) { // Check if it's an IP address
        struct hostent *host = gethostbyname(argv[1]);
        if (host == NULL) {
            perror("Unknown host");
            exit(1);
        }
        memcpy(&send_addr.sin_addr, host->h_addr, host->h_length);
    }
    // Send ICMP packets at regular intervals until interrupted by user (Ctrl+C)
    while (1) {
        sleep(1); // Sleep for 1 second before sending next packet
        alarm(1); // Set alarm to trigger after 1 second
    }
    close(ip_fd); // Close the raw socket when done
    return 0;
}

八、FAQs问答环节

问:为什么在发送ICMP包之前要将校验和字段置为0?

如何在C语言中实现Ping功能?

答:在发送ICMP包之前将校验和字段置为0是因为ICMP协议要求在传输过程中对整个包(包括校验和字段本身)进行校验和计算,通过将校验和字段先置为0,可以确保在计算校验和时不会受到之前值的影响,接收方在接收到ICMP包后也会重新计算校验和,并与包中的校验和字段进行比较,以验证数据的完整性,如果校验和不匹配,则说明数据在传输过程中发生了变化,该ICMP包将被丢弃。

问:在ping命令中,如何指定发送的数据包数量和间隔时间?

答:在Linux的ping命令中,可以通过-c选项来指定发送的数据包数量,例如ping -c 4 www.example.com表示发送4个数据包,通过-i选项可以指定发送数据包的间隔时间,例如ping -i 0.5 www.example.com表示每0.5秒发送一个数据包,这些选项使得ping命令更加灵活,可以根据实际需求进行调整。

原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1496396.html

本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。

(0)
未希
上一篇 2025-01-16 23:10
下一篇 2025-01-16 23:12

相关推荐

  • 如何在 C 中调用 JavaScript 代码?

    在C#中调用JavaScript代码,可以通过WebBrowser控件或使用第三方库如CefSharp。以下是一个简单的示例:,,“csharp,using System;,using System.Windows.Forms;,,public class MyForm : Form,{, private WebBrowser webBrowser;,, public MyForm(), {, webBrowser = new WebBrowser();, webBrowser.Dock = DockStyle.Fill;, this.Controls.Add(webBrowser);, , webBrowser.DocumentCompleted += WebBrowser_DocumentCompleted;, webBrowser.Navigate(“about:blank”);, },, private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e), {, webBrowser.Document.InvokeScript(“eval”, new object[] { “alert(‘Hello from C#’);” });, },, [STAThread], public static void Main(), {, Application.EnableVisualStyles();, Application.Run(new MyForm());, },},“,,这个示例展示了如何在C#中使用WebBrowser控件加载一个空白页面,并在页面加载完成后执行JavaScript代码。

    2025-01-16
    00
  • 如何使用C实现GET请求以访问网站?

    在C#中,你可以使用HttpClient类来发送GET请求访问网站。首先需要引入System.Net.Http命名空间,然后创建一个HttpClient实例,并使用其GetAsync方法发送GET请求。

    2025-01-16
    06
  • CJS完全加载,这是什么意思,又该如何实现?

    在JavaScript开发中,CommonJS是一种模块规范,它允许开发者以模块化的方式组织代码,CommonJS模块系统通常用于Node.js环境,但在现代前端框架如React Native、Electron等中也广泛使用,本文将详细介绍CommonJS模块的加载机制,包括其工作原理、优缺点以及与其他模块系统……

    2025-01-16
    06
  • 如何在C中实现登录时记住密码并存储到数据库?

    在C#中实现登录功能并记住密码,可以通过以下步骤完成:,,1. **用户界面设计**:创建一个包含用户名、密码输入框和“记住密码”复选框的登录表单。,2. **数据库设计**:在数据库中存储用户的用户名和加密后的密码。,3. **加密处理**:使用哈希算法(如SHA-256)对用户密码进行加密存储。,4. **登录验证**:用户输入用户名和密码后,系统将输入的密码进行加密并与数据库中的加密密码进行比对。,5. **记住密码功能**:如果用户勾选了“记住密码”,则将用户名和密码保存到本地文件或注册表中。下次启动程序时自动填充这些信息。,,以下是一个简单的示例代码片段,展示了如何实现上述功能:,,“csharp,using System;,using System.Data.SqlClient;,using System.Security.Cryptography;,using System.Text;,using System.Windows.Forms;,,public class LoginForm : Form,{, private TextBox txtUsername;, private TextBox txtPassword;, private CheckBox chkRememberMe;, private Button btnLogin;,, public LoginForm(), {, // 初始化控件…, btnLogin.Click += BtnLogin_Click;, },, private void BtnLogin_Click(object sender, EventArgs e), {, string username = txtUsername.Text;, string password = txtPassword.Text;, bool rememberMe = chkRememberMe.Checked;,, if (ValidateUser(username, password)), {, if (rememberMe), {, SaveCredentials(username, password);, }, MessageBox.Show(“登录成功!”);, }, else, {, MessageBox.Show(“用户名或密码错误!”);, }, },, private bool ValidateUser(string username, string password), {, string hashedPassword = GetHashedPassword(password);, string storedPassword = GetStoredPasswordFromDatabase(username);, return storedPassword == hashedPassword;, },, private string GetHashedPassword(string password), {, using (SHA256 sha256 = SHA256.Create()), {, byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));, StringBuilder builder = new StringBuilder();, foreach (byte b in bytes), {, builder.Append(b.ToString(“x2”));, }, return builder.ToString();, }, },, private string GetStoredPasswordFromDatabase(string username), {, // 从数据库获取加密后的密码…, return “”; // 返回加密后的密码字符串, },, private void SaveCredentials(string username, string password), {, // 将用户名和密码保存到本地文件或注册表…, },},“,,这个示例展示了基本的登录流程和记住密码功能的实现。实际应用中,还需要考虑更多的安全性措施,如防止SQL注入、使用更安全的密码存储方式等。

    2025-01-16
    00

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

产品购买 QQ咨询 微信咨询 SEO优化
分享本页
返回顶部
云产品限时秒杀。精选云产品高防服务器,20M大带宽限量抢购 >>点击进入