⚠⚠项目时遇到的新函数新问题小记xX

2023-11-01

1,gethostname() : 返回本地主机的标准主机名。

原型如下:
#include <unistd.h>
int gethostname(char *name, size_t len);
参数说明:
接收缓冲区name,其长度必须为len字节或是更长,存获得的主机名。
接收缓冲区name的最大长度

2,gethostbyname():用域名或主机名获取IP地址

包含头文件
#include <netdb.h>
#include <sys/socket.h>

函数原型
struct hostent *gethostbyname(const char *name);
传入值是域名或者主机名,例如"www.google.cn"等等。传出值,是一个hostent的结构。
返回hostent结构体类型指针,调用失败,返回NULL,hostent->h_addr_lisst表示的是主机的ip地址,注意,这个是以网络字节序存储的。千万不要直接用printf带%s参数来打这个东西,真正需要打印出这个IP的话,需要调用inet_ntop()。

  • 注意点:
    struct in_addr 结构体:表示一个32位的IPv4地址。
    in_addr_t一般为32位的unsigned int,其字节顺序为网络字节序,即该无符号数采用大端字节序。其中每8位表示一个IP地址中的一个数值。
    打印的时候可以调用inet_ntoa()函数将其转换为char*类型。
    头文件为:#include <arpa/inet.h>

获取本地主机,通过本地主机名获取本地ip

3,设置I/O为非阻塞模式代码小抄==

 14 void activate_nonblock(int fd);
 15 {
 16         int ret;
 17         int flags = fcntl(fd, F_GETFL);
 18         if(flags == -1)
 19                 EXX_EXIT("fcntl error");
 20         flags |= O_NONBLOCK;
 21         ret = fcntl(fd, F_SETFL, flags);
 22         if(ret == -1)
 23                 ERR_EXIT("fcntl error");
 24 }
 25 

3.2设置为阻塞模式

 void deactivate_nonblock(int fd)
 27 {
 28         int ret;
 29         int flags = fcntl(fd, F_GETFL);
 30         if(flags == -1)
 31                 ERR_EXIT("fcntl");
 32         flags &= ~O_NONBLOCK;
 33         ret = fcntl(fd, F_SETFL, flags);
 34         if(ret == -1)
 35                 ERR_EXIT("fcntl");
 36 
 37 }

4,fd_set是一组文件描述符字的集合

  • FD_ZERO(fd_set *fdset);//将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。

  • FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。

  • FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。

  • FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。
    原文链接:https://blog.csdn.net/sin0803/article/details/36192779

5,getsockopt()函数用于获取任意类型、任意状态套接口的选项当前值

int getsockopt(int socket, int level, int option_name,
            void *restrict option_value, socklen_t *restrict option_len);

功能:获取一个套接字的选项
参数:
socket:文件描述符
level:协议层次
SOL_SOCKET 套接字层次
IPPROTO_IP ip层次
IPPROTO_TCP TCP层次

option_name: 选项的名称(套接字层次)
SO_BROADCAST 是否允许发送广播信息
SO_REUSEADDR 是否允许重复使用本地地址
SO_SNDBUF 获取发送缓冲区长度
SO_RCVBUF 获取接收缓冲区长度
SO_RCVTIMEO 获取接收超时时间
SO_SNDTIMEO 获取发送超时时间
option_value: 获取到的选项的值
option_len: value的长度
返回值:
成功:0
失败:-1

6,⭐⭐发送文件描述符

void send_fd(int sock_fd, int fd)
{
	int ret;
	struct msghdr msg;
	struct cmsghdr *p_cmsg;
	struct iovec vec;
	char cmsgbuf[CMSG_SPACE(sizeof(fd))];
	int *p_fds;
	char sendchar = 0;
	msg.msg_control = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);
	p_cmsg = CMSG_FIRSTHDR(&msg);
	p_cmsg->cmsg_level = SOL_SOCKET;
	p_cmsg->cmsg_type = SCM_RIGHTS;
	p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
	p_fds = (int*)CMSG_DATA(p_cmsg);
	*p_fds = fd;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &vec;
	msg.msg_iovlen = 1;
	msg.msg_flags = 0;

	vec.iov_base = &sendchar;
	vec.iov_len = sizeof(sendchar);
	ret = sendmsg(sock_fd, &msg, 0);
	if (ret != 1)
		ERR_EXIT("sendmsg");
}

struct msghdr结构体,用于接收来自应用层的数据包

#include<sys/socket.h>

struct msghdr  { 
    void  * msg_name ;   / *  消息的协议地址  * / 协议地址和套接口信息,在非连接的UDP中,发送者要指定对方地址端口,接受方用于的到数据来源,如果不需要的话可以设置为NULL(在TCP或者连接的UDP中,一般设置为NULL)
    socklen_t msg_namelen ;   / *  地址的长度  * / 
    struct iovec  * msg_iov ;   / *  多io缓冲区的地址  * / 
     int  msg_iovlen ;   / *  缓冲区的个数  * / 
    void  * msg_control ;   / *  辅助数据的地址  * / 
    socklen_t msg_controllen ;   / *  辅助数据的长度  * / 
     int  msg_flags ;   / *  接收消息的标识  * / 
} ;

7,⭐⭐接收文件描述符

int recv_fd(const int sock_fd)
{
	int ret;
	struct msghdr msg;
	char recvchar;
	struct iovec vec;
	int recv_fd;
	char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
	struct cmsghdr *p_cmsg;
	int *p_fd;
	vec.iov_base = &recvchar;
	vec.iov_len = sizeof(recvchar);
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &vec;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);
	msg.msg_flags = 0;

	p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
	*p_fd = -1;  
	ret = recvmsg(sock_fd, &msg, 0);
	if (ret != 1)
		ERR_EXIT("recvmsg");

	p_cmsg = CMSG_FIRSTHDR(&msg);
	if (p_cmsg == NULL)
		ERR_EXIT("no passed fd");


	p_fd = (int*)CMSG_DATA(p_cmsg);
	recv_fd = *p_fd;
	if (recv_fd == -1)
		ERR_EXIT("no passed fd");

	return recv_fd;
}

8,begin_session,socketpair进行父子进程间通信

void begin_session(session_t *sess)
{
	activate_oobinline(sess->ctrl_fd);
	/*
	int sockfds[2];    //内部进程间通道的建立
	if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0) //创建了一对无名的套接字描述符 和管道类似
		ERR_EXIT("socketpair");
    */
 
	priv_sock_init(sess);
 
	pid_t pid;
	pid = fork();
	if (pid < 0)
		ERR_EXIT("fork");
 
	if (pid == 0)//子进程
	{
		// ftp服务进程    //与外界进行交互通讯,接受客户端发送过来的命令请求
		/*
		close(sockfds[0]);
		sess->child_fd = sockfds[1];
		*/
		priv_sock_set_child_context(sess);
		handle_child(sess);
		 //设置子进程通讯套接字
	}
	else
	{
		// nobody进程    将父进程改为nobody进程, 父进程接收的是子进程发送接受的 仅仅是起到一些辅助的作用
		//内部子进程,只是起辅助作用不与外部进行通讯
		/*close(sockfds[1]);
		sess->parent_fd = sockfds[0];*/
		priv_sock_set_parent_context(sess);
		handle_parent(sess);
	}
}

将(父)进程转换成nobody进程


发生段错误

进入gdb环境,r运行一下,bt追踪端错误

指针指向一个字符串,字符串时常量,常量是不能修改的,容易出现段错误,不如就用数组

extern关键字

百科名片:extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。另外,extern也可用来进行链接指定。

extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”

各个文件中定义的全局变量名不可相同。
在链接阶段,各个文件的内容(实际是编译产生的obj文件)是被合并到一起的,因而,定义于某文件内的全局变量,在链接完成后,它的可见范围被扩大到了整个程序。

extern int a; 仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。

引用一个定义在其它模块的全局变量或函数(如,全局函数或变量定义在A模块,B欲引用)有两种方法,一、B模块中include模块A的头文件。二、模块B中对欲引用的模块A的变量或函数重新声明一遍,并前加extern关键字。 与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,

忘了就再看这个:https://www.cnblogs.com/Azhu/articles/2454483.html

fclose()和close()

fclose()是与文件流相关的函数.在fopen()的帮助下打开文件并将流分配给FILE * ptr.然后,您将使用fclose()关闭打开的文件.

close()是与文件描述符相关的函数.在open()的帮助下打开文件并将描述符赋值给int fd.然后,您将使用close()关闭打开的文件.

fopen(),fclose()等函数是C标准函数,而open(),close()等其他类别是POSIX特有的.这意味着用open(),close()等编写的代码不是标准的C代码,因此是不可移植的.用fopen(),fclose等编写的代码是标准代码,可以移植到任何类型的系统上.


为什么使用多进程而不是多线程?
​ 原因是在多线程或IO复用的情况下,当前目录是共享的,无法根据每一个连接来拥有自己的当前目录,也就是说当前用户目录的切换会影响到其他的用户。


出现的问题

Segmentation fault (core dumped)

eyedebug是不可能的,
方法1:
用dmesg和addr2line查错
dmesg | grep miniftpd
addr2line -e miniftpd core地址

在这里插入图片描述方法2
在这里插入图片描述
也可以得到
在这里插入图片描述

感谢https://blog.csdn.net/stpeace/article/details/49806473,gdb调试要在多看看这文章(●’◡’●)

写代码时一定要多多注意指针,地址,定义的时候传值的时候都要过脑子考虑呀,改bug真是太费劲了


判断密码是否正确的sp_pwdp

其中passwd 是用户输入的密码,sp->sp_pwdp是从shaodw文件中取得的密文,

通过crypt函数把用户输入的明文重新加工为密文,然后比较用户原来的密文,和加工出来的

密文是否相同,判断登录是否成功.

char *newpwd;
struct spwd *sp;
sp = getspnam(name);
if (NULL == sp) return 1;
newpwd = crypt(passwd,sp->sp_pwdp);
return ( strcmp ( newpwd, sp->sp_pwdp ) == 0 )?0:1;

send_fd( , ), recv_fd(),传送和发送文件描述符

传送进程描述符,简单的来说,就是进程A打开一个文件f,获得了一个文件描述符fd1,然后进程A将该描述符通过某些方式,传递给了B,此时B就具有了描述符fd2(注意,fd1 不一定等于fd2),从而可以通过fd2对文件f进行读写等一系列的操作。其实本质上

相当于A,B两个进程同时打开了文件f。

具体实现其实比较简单,例如当一个父进程要向子进程传递一个文件描述符时,首先会在fork产生子进程以前,调用socketpair建立一个套接字对,用于父子进程之间的通信。之后父进程打开文件f,获得文件描述符fd1。接着通过sendmsg将包含文件描述符fd1的消息发送出去,而在子进程通过recvmsg接收消息,从中获取出文件描述符fd2。最后,子进程就能通过操作fd2对文件f进行一系列的读写操作。

最后,需要注意的是,当发送进程将文件描述符传送给接收进程以后,通常会关闭该描述符。不过,发送进程关闭该描述符并不会真的关闭该文件或设备,其原因是该文件描述符仍然视为由接收进程打开(即使接收进程尚未接收到该描述符,此时称该描述符在飞行中…in flight)


在写函数的时候,尤其是读写函数的时候,什么时候传递地址,什么时候直接传递,要分的清

会有什么问题

是否有资源泄露的问题,如有无内存泄露,是否存在句柄打开而为关闭的情况
尽可能详细说明每个函数的时间复杂度和空间复杂度,

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

⚠⚠项目时遇到的新函数新问题小记xX 的相关文章

  • 通过 Visual Studio 2017 使用远程调试时 Linux 控制台输出在哪里?

    我的Visual Studio 2017 VS2017 成功连接Linux系统 代码如下 include
  • 无需超级用户即可在 Linux 中打开 RAW 套接字

    我必须编写一个在 Linux 上运行的 ping 函数 语言是 C 所以 C 也可以 在网上搜索并查看源代码ping命令 事实证明我应该创建一个原始套接字 icmp sock socket AF INET SOCK RAW IPPROTO
  • 添加文件时运行 shell 命令

    我的 Linux 机器上有一个名为 images 的文件夹 该文件夹连接到一个网站 该网站的管理员可以向该网站添加图片 但是 当添加图片时 我想要一个命令来运行调整目录中所有图片的大小 简而言之 我想知道当新文件添加到特定位置时如何使服务器
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • 使用包管理器时如何管理 Perl 模块?

    A 最近的问题 https stackoverflow com questions 397817 unable to find perl modules in intrepid ibex ubuntu这让我开始思考 在我尝试过的大多数 Li
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac
  • 绕过 dev/urandom|random 进行测试

    我想编写一个功能测试用例 用已知的随机数值来测试程序 我已经在单元测试期间用模拟对其进行了测试 但我也希望用于功能测试 当然不是全部 最简单的方法是什么 dev urandom仅覆盖一个进程 有没有办法做类似的事情chroot对于单个文件并
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • 这种文件锁定方法可以接受吗?

    我们有 10 个 Linux 机器 每周必须运行 100 个不同的任务 这些计算机主要在我们晚上在家时执行这些任务 我的一位同事正在开发一个项目 通过使用 Python 自动启动任务来优化运行时间 他的程序将读取任务列表 抓取一个打开的任务
  • 如何在 Mac OSX Mavericks 中正确运行字符串工具?

    如何在 Mac OSX Mavericks 中正确运行字符串工具 我尝试按照我在网上找到的示例来运行它 strings a UserParser class 但我收到此错误 错误 Applications Xcode app Content
  • 为什么同一个curl命令在windows和linux下输出不同的东西?

    为什么同样的curl o file https www link com 命令输出不同的东西 例如 如果我运行命令curl o source txt https www youtube com playlist list PLIx6Fwnp
  • 尽管我已在 python ctypes 中设置了信号处理程序,但并未调用它

    我尝试过使用 sigaction 和 ctypes 设置信号处理程序 我知道它可以与python中的信号模块一起使用 但我想尝试学习 当我向该进程发送 SIGTERM 时 但它没有调用我设置的处理程序 只打印 终止 为什么它不调用处理程序
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • Linux下显卡内存使用情况

    Linux下有哪些工具可以监控显卡内存使用情况 NVIDIA 性能套件 http developer nvidia com content nvidia perfkit有Linux版本 可以实时监控各种显卡属性 包括显卡内存使用情况 显然
  • 如何在文件中搜索多行模式?

    我需要找到包含特定字符串模式的所有文件 我想到的第一个解决方案是使用find管道与xargs grep find iname py xargs grep e YOUR PATTERN 但是 如果我需要查找跨越多行的模式 我就会陷入困境 因为
  • 使用awk将列中的值替换为txt文件中的另一个值

    我是 Linux 和 awk 脚本编写的新手 我有 tab delim txt 文件 如下所示 AAA 134 145 Sat 150 167 AAA 156 167 Sat 150 167 AAA 175 187 Sat 150 167
  • 在 C 中运行 setuid 程序的正确方法

    我有一个权限为4750的进程 我的Linux系统中存在两个用户 root 用户和 appz 用户 该进程继承以 appz 用户身份运行的进程管理器的权限 我有两个基本惯例 void do root void int status statu

随机推荐