深入剖析Linux C编程中的ntohl函数 (linux c ntohl)

在Linux C编程中,ntohl函数是网络字节序和主机字节序之间转换的重要函数。由于网络中传输的数据在通信过程中可能会出现字节序不一致的情况,因此需要通过ntohl函数将字节序进行转换,从而保证数据的正确传输和处理。本文将深入剖析ntohl函数的使用、作用原理及其在网络编程中的应用。

一、ntohl函数的使用

ntohl函数是Linux系统中的网络字节序转换函数之一,其函数原型如下:

uint32_t ntohl(uint32_t netlong);

参数netlong为需要转换的32位无符号整型数据,函数返回值为转换后的32位无符号整型数据。ntohl函数会将参数netlong从网络字节序转换为主机字节序,通常情况下,主机字节序是指大端字节序(MSB在前,LSB在后)。

在网络编程中,ntohl函数通常和htons、ntohs、htonl等函数搭配使用,用于对传输的数据进行字节序的转换。比如,如果需要将一个32位整数在网络中传输,需要把它转换为网络字节序,使用htons函数可以将它转换为16位网络字节序,使用htonl函数可以将它转换为32位网络字节序,传输过程中再通过ntohl/ntohs函数将它转换为主机字节序。

二、ntohl函数的作用原理

在计算机中,数据在存储时以字节为单位进行存储,不同的计算机硬件架构决定了不同的字节序。大端字节序(MSB在前,LSB在后)也称为网络字节序,小端字节序(LSB在前,MSB在后)也称为主机字节序。不同的系统(Windows、Linux、Mac OS等)具有不同的字节序,因此在网络通信中,需要将不同字节序的数据进行转换,以保证数据的正确传输和处理。

ntohl函数的作用就是将32位无符号整型数据从网络字节序转换为主机字节序,其转换原理如下:

假设我们要对一个32位无符号整型值0x12345678进行字节序转换,这个值的二进制表示为

00010010 00110100 01010110 01111000

– 在大端字节序的计算机中,该值按照从高到低的顺序存储,因此它在计算机中的表示为0x12345678,在网络中的表示也应该为0x12345678。

– 在小端字节序的计算机中,该值按照从低到高的顺序存储,因此它在计算机中的表示为0x78563412,在网络中的表示应该为0x78563412。

– 当数据从小端字节序计算机发送到大端字节序计算机时,需要把该值转换为大端字节序,先将低位字节0x78放在之一个字节,高位字节0x12放在第四个字节,中间两个字节以此类推,得到的结果为0x78563412。然后接收方可以根据该值的字节序将它转换为主机字节序。

– 当数据从大端字节序计算机发送到小端字节序计算机时,需要把该值转换为小端字节序,方法与上述相仿。

ntohl函数的作用就是将参数netlong从网络字节序转换为主机字节序,其实现原理与上述类似。当ntohl函数在大端字节序计算机中执行时,该函数不做任何转换,直接将参数netlong返回;当ntohl函数在小端字节序计算机中执行时,该函数将参数netlong的字节序进行转换,然后将转换结果返回。

三、ntohl函数在网络编程中的应用

在网络编程中,ntohl函数通常和htonl、htons、ntohs等函数搭配使用,用于对传输的数据进行字节序的转换。以下是ntohl函数在网络编程中的应用示例:

– 客户端向服务器发送一个32位整数数据:

uint32_t data = 0x12345678;

data = htonl(data); // 将数据转换为网络字节序

send(sockfd, &data, sizeof(data), 0); // 通过网络发送数据

– 服务器接收到客户端发送的数据后进行处理:

uint32_t data;

recv(sockfd, &data, sizeof(data), 0); // 从网络接收数据

data = ntohl(data); // 将数据从网络字节序转换为主机字节序

// 对数据进行处理

通过使用ntohl函数,程序可以轻松地对传输的数据进行字节序转换,使得数据在不同的计算机之间正确传输和处理,在网络编程中具有重要的实用价值。

结语

本文深入剖析了Linux C编程中的ntohl函数的用法、作用原理及其在网络编程中的应用。我们了解了ntohl函数的转换原理,了解了网络通讯中字节序转换的必要性,通过本文,我们可以更好地掌握ntohl函数的使用技巧,并在实际编程中更加熟练地运用它。

相关问题拓展阅读:

linux little endian和big endian是什么意思

1.故事的起源 “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。 我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。 2.什么是Big Endian和Little Endian? 在设计计算伏辩机系统的时候,有两种处理内存中数据的方法。一种叫为little-endian,存放在内存中更低位的数值是来自数据的最右边部分(也就是数据的更低位部分)。 比如某些文件需要在不同平台处理,或者通过Socket通信。这方面我们可以借助ntohl(), ntohs(), htonl(), and htons()函数进行格式转换, 个人补充:一个操作数作htonl或ntohl结果不一定相同,当机器字节序跟网络字节序刚好是仅仅big endian和little endian的区别时是相同的。 3. 如何理解Big Endian和Little Endian 举个例子: int a = 1; a这个数本身的16进制表示是0x在内存中怎么存储呢? 如果你的CPU是intel x86架构的(基本上就是通常我们说的奔厅宽腾cpu),那么就是0x01 0x00 0x00 0x00 , 这也就是所谓扮厅亮的little-endian, 低字节存放在内存的低位. 如果你的CPU是老式AMD系列的(很老很老的那种,因为最新的AMD系列已经是x86架构了), 它的字节序就是big-endian, 其内存存储就是 0x00 0x00 0x00 0x01在内存中从高字节开始存放。 现在世界上绝大多数的CPU都是little-endian。 4. 了解big-endian和little-endian有什么作用? 一个重要的作用就是了解在网络上不同的机器间的数据如何传输。 假设我们在网络上有两台机器A和B,

Ubuntu编译了新的内核,进入新内核时一直显示载入Linux 5.6.7,载入初始化内存盘咋回事?

概述====1)当内核配置了内存盘时, 内核在初始化时可以将

软盘

加载到内存盘中作为根盘.当同时配置了初始化内存盘(Initail RAM Disk)时, 内核在初始化时可以在安装主盘之前,通过引导程序所加载的initrd文件建立一个内存初始化盘, 首先将它安装成根

文件系统

, 然后执行其

根目录

下的linuxrc 文件,可用于在安装主盘之前加载一些内核模块. 等到linuxrc 程序退出后, 再将主盘安装成根文件系统,并将内存初始化盘转移安皮银装到其/initrd目录下.2)当主盘就是initrd所生成的内存初始化盘时, 不再进行重新安装,在DOS下用loadlin加载的抢救盘就是这种工作方式.3)引导程序所加载的initrd为文件系统的映象文件, 可以是gzip压缩的, 也可以是不压缩的.能旁枝够识别的文件系统有minix,ext2,romfs三种.4)当内核的根盘为软盘时,内核初始化运握敏时会测试软盘的指定部位是否存在文件系统或压缩文件映象, 然后将之加载或解压到内存盘中作为根盘. 这是单张抢救软盘的工作方式.有关代码========; init/main.c#ifdef CONFIG_BLK_DEV_INITRDkdev_t real_root_dev; 启动参数所设定的根盘设备#endifalinkage void __init start_kernel(void){ char * command_line; unsigned long mempages; extern char saved_command_line; lock_kernel(); printk(linux_banner); setup_arch(&command_line);arch/i386/kernel/setup.c中,初始化initrd_start和initrd_end两个变量 …#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start need_resched = 1; cpu_idle();}static int init(void * unused){ lock_kernel(); do_basic_setup(); /* * Ok, we have completed the initial bootup, and * we’re essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. */ free_initmem(); unlock_kernel(); if (open(“/dev/console”, O_RDWR, 0) 0) while (pid != wait(&i)); 等待linuxrc进程退出 if (MAJOR(real_root_dev) != RAMDISK_MAJOR|| MINOR(real_root_dev) != 0) { ; 如果原来的根盘不是0号内存盘,则使用原来的根文件系统, ; 并且将内存盘转移到其/initrd目录下 error = change_root(real_root_dev,”/initrd”); if (error) printk(KERN_ERR “Change root to /initrd: “”error %d/n”,error); } }#endif}#ifdef CONFIG_BLK_DEV_INITRDstatic int do_linuxrc(void * shell){ static char *argv = { “linuxrc”, NULL, }; close(0);close(1);close(2); setsid(); 设置新的session号 (void) open(“/dev/console”,O_RDWR,0); (void) dup(0); (void) dup(0); return execve(shell, argv, envp_init);}#endif; arch/i386/kernel/setup.c#define RAMDISK_IMAGE_START_MASK 0x07FF#define RAMDISK_PROMPT_FLAG 0x8000#define RAMDISK_LOAD_FLAG 0x4000 #define PARAM ((unsigned char *)empty_zero_page)#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) 可用rdev设置的参数#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))#define INITRD_START (*(unsigned long *) (PARAM+0x218)) 初始化盘映象起始物理地址#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) 初始化盘字节数void __init setup_arch(char **cmdline_p){ …#ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; 以块为单位 rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);#endif …#ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE 0x%08lx)/ndisabling initrd/n”,INITRD_START + INITRD_SIZE,max_low_pfn (rd_length >> BLOCK_SIZE_BITS)) { ; 如果输入盘的尺寸超过了输出内存盘的允许尺寸 printk(“RAMDISK: image too big! (%d/%ld blocks)/n”,nblocks, rd_length >> BLOCK_SIZE_BITS); goto done; } /* * OK, time to copy in the data */ buf = kmalloc(BLOCK_SIZE, GFP_KERNEL); if (buf == 0) { printk(KERN_ERR “RAMDISK: could not allocate buffer/n”); goto done; } if (blk_size) devblocks = blk_size; 取输入盘的容量#ifdef CONFIG_BLK_DEV_INITRD if (MAJOR(device) == MAJOR_NR && MINOR(device) == INITRD_MINOR) devblocks = nblocks; 如果输入是初始化内存盘,则盘的容量为它的实际尺寸#endif if (devblocks == 0) { printk(KERN_ERR “RAMDISK: could not determine device size/n”); goto done; } printk(KERN_NOTICE “RAMDISK: Loading %d blocks into ram disk… “, nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? “s” : “”); for (i=0; i release) infile.f_op->release(inode, &infile); printk(“Please insert disk #%d and press ENTER/n”, i/devblocks+1); wait_for_keypress(); if (blkdev_open(inode, &infile) != 0) { printk(“Error opening disk./n”); goto done; } infile.f_pos = 0; printk(“Loading disk #%d… “, i/devblocks+1); } infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos); outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos);#if !defined(CONFIG_ARCH_S390) if (!(i % 16)) { printk(“%c/b”, rotator); rotate++; }#endif } printk(“done./n”); kfree(buf);successful_load: invalidate_buffers(device); ROOT_DEV = MKDEV(MAJOR_NR, unit); 将根盘设备设置为当前加载的内存盘 if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, “rd/0″);done: if (infile.f_op->release) infile.f_op->release(inode, &infile); set_fs(fs); return;free_inodes: /* free inodes on error */ iput(out_inode); blkdev_put(inode->i_bdev, BDEV_FILE);free_inode: iput(inode);}int __init identify_ramdisk_image(kdev_t device, struct file *fp, int start_block){ const int size = 512; struct minix_super_block *minix; struct ext2_super_block *ext2; struct romfs_super_block *romf; int nblocks = -1; unsigned char *buf; buf = kmalloc(size, GFP_KERNEL); if (buf == 0) return -1; minix = (struct minix_super_block *) buf; ext2 = (struct ext2_super_block *) buf; romf = (struct romfs_super_block *) buf; memset(buf, 0xe5, size); /* * Read block 0 to test for gzipped kernel */ if (fp->f_op->llseek) fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; fp->f_op->read(fp, buf, size, &fp->f_pos); ; 读取offset开始的512字节 /* * If it matches the gzip magic numbers, return -1 */ if (buf == 037 && ((buf == 0213) || (buf == 0236))) { printk(KERN_NOTICE”RAMDISK: Compressed image found at block %d/n”,start_block); nblocks = 0; goto done; } /* romfs is at block zero too */ if (romf->word0 == ROMSB_WORD0 && romf->word1 == ROMSB_WORD1) { printk(KERN_NOTICE”RAMDISK: romfs filesystem found at block %d/n”,start_block); nblocks = (ntohl(romf->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; goto done; } /* * Read block 1 to test for minix and ext2 superblock */ if (fp->f_op->llseek) fp->f_op->llseek(fp, (start_block+1) * BLOCK_SIZE, 0); fp->f_pos = (start_block+1) * BLOCK_SIZE; fp->f_op->read(fp, buf, size, &fp->f_pos); /* Try minix */ if (minix->s_magic == MINIX_SUPER_MAGIC || minix->s_magic == MINIX_SUPER_MAGIC2) { printk(KERN_NOTICE”RAMDISK: Minix filesystem found at block %d/n”,start_block); nblocks = minix->s_nzones s_log_zone_size; goto done; } /* Try ext2 */ if (ext2->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) { printk(KERN_NOTICE”RAMDISK: ext2 filesystem found at block %d/n”,start_block); nblocks = le32_to_cpu(ext2->s_blocks_count); goto done; } printk(KERN_NOTICE”RAMDISK: Couldn’t find valid RAM disk image starting at %d./n”,start_block);done: if (fp->f_op->llseek) fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; kfree(buf); return nblocks;}; fs/super.cvoid __init mount_root(void){ struct file_system_type * fs_type; struct super_block * ; struct vfount *vfnt; struct block_device *bdev = NULL; mode_t mode; int retval; void *handle; char path; int path_start = -1;#ifdef CONFIG_BLK_DEV_FD if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { 当根盘还是软盘,表示没有加载过内存盘#ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; extern void rd_load_secondary(void);#endif floppy_eject();#ifndef CONFIG_BLK_DEV_RAM printk(KERN_NOTICE “(Warning, this kernel has no ramdisk support)/n”);#else /* rd_doload is 2 for a dual initrd/ramload setup */ ; 只有当加载了initrd但没有释放到内存盘中(mount_inird=0)才有可能到这一步 if(rd_doload==2) rd_load_secondary(); 加载另一张软盘到1号内存盘作为根盘 else#endif { printk(KERN_NOTICE “VFS: Insert root floppy and press ENTER/n”); wait_for_keypress(); } }#endif devfs_make_root (root_device_name); handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME, MAJOR (ROOT_DEV), MINOR (ROOT_DEV),DEVFS_SPECIAL_BLK, 1); if (handle) /* Sigh: bd*() functions only paper over the cracks */ { unsigned major, minor; devfs_get_maj_min (handle, &major, &minor); ROOT_DEV = MKDEV (major, minor); } /* * Probably pure paranoia, but I’m less than happy about delving into * devfs crap and checking it right now. Later. */ if (!ROOT_DEV) panic(“I have no root and I want to scream”); bdev = bdget(kdev_t_to_nr(ROOT_DEV)); if (!bdev) panic(__FUNCTION__ “: unable to allocate root device”); bdev->bd_op = devfs_get_ops (handle); path_start = devfs_generate_path (handle, path + 5, sizeof (path) – 5); mode = FMODE_READ; if (!(root_mountflags & MS_RDON)) mode |= FMODE_WRITE; retval = blkdev_get(bdev, mode, 0, BDEV_FS); if (retval == -EROFS) { root_mountflags |= MS_RDON; retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS); } if (retval) {/* * Allow the user to distinguish between failed open * and bad superblock on root device. */ printk (“VFS: Cannot open root device /”%s/” or %s/n”, root_device_name, kdevname (ROOT_DEV)); printk (“Please append a correct /”root=/” boot option/n”); panic(“VFS: Unable to mount root fs on %s”, kdevname(ROOT_DEV)); } check_disk_change(ROOT_DEV); = get_super(ROOT_DEV); 取根盘的超级块 if () { fs_type = ->s_type; goto mount_it; } read_lock(&file_systems_lock); for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) { if (!(fs_type->fs_flags & FS_REQUIRES_DEV)) continue; 根文件系统必须依赖于块设备 if (!try_inc_mod_count(fs_type->owner)) continue; 当文件系统模块正在删除过程中 read_unlock(&file_systems_lock); = read_super(ROOT_DEV,bdev,fs_type,root_mountflags,NULL,1);建立根盘的超级块结构 if () goto mount_it; read_lock(&file_systems_lock); put_filesystem(fs_type); 释放对文件系统模块的引用 } read_unlock(&file_systems_lock); panic(“VFS: Unable to mount root fs on %s”, kdevname(ROOT_DEV));mount_it: printk (“VFS: Mounted root (%s filesystem)%s./n”, fs_type->name, (->s_flags & MS_RDON) ? ” readonly” : “”); if (path_start >= 0) { devfs_mk_symlink (NULL, “root”, DEVFS_FL_DEFAULT, path + 5 + path_start, NULL, NULL); memcpy (path + path_start, “/dev/”, 5); vfnt = add_vfnt(NULL, ->s_root, path + path_start); } else vfnt = add_vfnt(NULL, ->s_root, “/dev/root”); 建立根盘的安装结构 /* FIXME: if something will try to umount us right now… */ if (vfnt) { set_fs_root(current->fs, vfnt, ->s_root); 设置当前进程的根盘和根目录 set_fs_pwd(current->fs, vfnt, ->s_root); 设置当前进程的当前盘和当前目录 if (bdev) bdput(bdev); /* holds a reference */ return; } panic(“VFS: add_vfnt failed for root fs”);}#ifdef CONFIG_BLK_DEV_INITRDint __init change_root(kdev_t new_root_dev,const char *put_old){ 以new_root_dev作为根盘重新安装根文件系统,原来的根转移到put_old目录下 struct vfount *old_rootmnt; struct nameidata devfs_nd, nd; int error = 0; read_lock(¤t->fs->lock); old_rootmnt = mntget(current->fs->rootmnt); 取当前进程的根盘安装结构 read_unlock(¤t->fs->lock); /* First unmount devfs if mounted */ if (path_init(“/dev”, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd)) error = path_walk(“/dev”, &devfs_nd); if (!error) { if (devfs_nd.mnt->mnt_->s_magic == DEVFS_SUPER_MAGIC && devfs_nd.dentry == devfs_nd.mnt->mnt_root) { dput(devfs_nd.dentry); down(&mount_sem); /* puts devfs_nd.mnt */ do_umount(devfs_nd.mnt, 0, 0); up(&mount_sem); } else path_release(&devfs_nd); } ROOT_DEV = new_root_dev; mount_root(); 改变根盘设备重新安装根文件系统#if 1 shrink_dcache(); 清除目录项缓冲中所有自由的目录项 printk(“change_root: old root has d_count=%d/n”,atomic_read(&old_rootmnt->mnt_root->d_count));#endif mount_devfs_fs (); /* * Get the new mount directory */ error = 0; if (path_init(put_old, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd)) error = path_walk(put_old, &nd); 在新的根盘中寻找put_old目录 if (error) { int blivet; printk(KERN_NOTICE “Trying to unmount old root … “); blivet = do_umount(old_rootmnt, 1, 0); 卸载原始的根盘 if (!blivet) { printk(“okay/n”); return 0; } printk(KERN_ERR “error %d/n”, blivet); return error; } /* FIXME: we should hold i_zombie on nd.dentry */ move_vfnt(old_rootmnt, nd.dentry, nd.mnt, “/dev/root.old”); mntput(old_rootmnt); path_release(&nd); return 0;}#endifstatic struct vfount *add_vfnt(struct nameidata *nd, 在虚拟文件系统中的安装点 struct dentry *root, 安装盘的根目录项 const char *dev_name) 安装盘名称{ struct vfount *mnt;

————————————————

版权声明:本文为CSDN博主「huanghaibin」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:

返回列表

上一篇:linux c http编程

关于linux c ntohl的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 深入剖析Linux C编程中的ntohl函数 (linux c ntohl)