96SEO 2026-02-20 07:43 6
。

一个进程在向管道写入数据后#xff0c;另一进程就可以从管道的另一端将其读取出来。
管道实际是用于进程间通信的一段共享内存创建管道的进程称为管道服务器连接到一个管道的进程为管道客户机。
一个进程在向管道写入数据后另一进程就可以从管道的另一端将其读取出来。
管道是半双工的数据只能向一个方向流动需要双方通信时需要建立起两个管道只能用于父子进程或者兄弟进程之间具有亲缘关系的进程。
比如fork或exec创建的新进程在使用exec创建新进程时需要将管道的文件描述符作为参数传递给exec创建的新进程。
当父进程与使用fork创建的子进程直接通信时发送数据的进程关闭读端接受数据的进程关闭写端。
单独构成一种独立的文件系统管道对于管道两端的进程而言就是一个文件但它不是普通的文件它不属于某种文件系统而是自立门户单独构成一种文件系统并且只存在与内存中。
数据的读出和写入一个进程向管道中写的内容被管道另一端的进程读出。
写入的内容每次都添加在管道缓冲区的末尾并且每次都是从缓冲区的头部读出数据。
管道的一端连接一个进程的输出。
这个进程会向管道中放入信息。
管道的另一端连接一个进程的输入这个进程取出被放入管道的信息。
一个缓冲区不需要很大它被设计成为环形的数据结构以便管道可以被循环利用。
当管道中没有信息的话从管道中读取的进程会等待直到另一端的进程放入信息。
当管道被放满信息的时候尝试放入信息的进程会等待直到另一端的进程取出信息。
当两个进程都终结的时候管道也自动消失。
创建一个简单的管道若成功则为数组fd分配两个文件描述符其中fd[0]
1管道通信是单向的并且遵守先进先出的原则即先写入的数据先读出。
管道把一个进程的标准输出和另一个进程的标准输入连接在一起。
数据读出后就意味着从管道中移走了消失了。
其它的进程都不能
pipe这种管道用于两个有亲缘关系的进程之间。
eg:父子进程…
父进程调用pipe函数创建管道得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
父进程调用fork创建子进程那么子进程也有两个文件描述符指向同一管道。
父进程关闭管道读端子进程关闭管道写端。
父进程可以向管道中写入数据子进程将管道中的数据读出。
由于管道是利用环形队列实现的数据从写端流入管道从读端流出这样就实现了进程间通信。
sizeof(buf));write(STDOUT_FILENO,
strlen(p));wait(NULL);close(fd[1]);}return
的创建进程不存在亲缘关系的进程只要可以访问该路径就能够彼此通过
命名管道FIFO)和无名管道pipe有一些特点是相同的不一样的地方在于:
后期的操作把这个命名管道当做普通文件一样进行操作open()、write()、read()、close()。
但是和无名管道一样操作命名管道肯定要考虑默认情况下其阻塞特性。
https://blog.csdn.net/lianghe_work/article/details/47722175
简单一句话只读等着只写只写等着只读只有两个都执行到才会往下执行。
4通信过程中读进程退出后写进程向命名管道内写数据时写进程也会收到SIGPIPE
4和5这两个特点和无名管道是一样的这里不再验证详情请看《无名管道》。
软中断是执行中断指令产生的而硬中断是由外设引发的。
https://zhuanlan.zhihu.com/p/85597791
软中断信号(signal又简称为信号)用来通知进程发生了异常事件。
进程之间可以互相通过系统调用kill发送软中断信号。
内核也可以因为内部事件而给进程发送信号通知进程发生了某个事件。
注意信号只是用来通知某进程发生了什么事件并不给该进程传递任何数据。
signal的执行点可以理解成从内核态返回用户态时在返回时如果发现待执行进程存在被触发的signal那么在离开内核态之后也就是将CPU切换到用户模式执行用户进程为该signal绑定的signal处理函数从这一点上看signal处理函数是在用户进程上下文中执行的。
当执行完signal处理函数之后再返回到用户进程被中断或者system
Signal机制实现的比较灵活用户进程由于中断或者system
call陷入内核之后将断点信息都保存到了堆栈中在内核返回用户态时如果存在被触发的signal那么直接将待执行的signal处理函数push到堆栈中在CPU切换到用户模式之后直接pop堆栈就可以执行signal处理函数并且返回到用户进程了。
Signal处理函数应用了进程上下文并且应用实际的中断模拟了进程的软中断过程。
如果信号发送给一个正在睡眠的进程那么要看该进程进入睡眠的优先级如果进程睡眠在可被中断的优先级上则唤醒进程否则仅设置进程表中信号域相应的位而不唤醒进程。
这一点比较重要因为进程检查是否收到信号的实际是一个进程在即将从内核态返回到用户态时或者在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。
内核处理一个进程收到的信号实际是在一个进程从内核态返回用户态时所以当一个进程在内核态下运行时软中断信号并不立即起作用要等到将返回用户态时才处理
核处理一个进程收到的软中断信号是在该进程的上下文中因此进程必须处于运行状态。
参考https://blog.csdn.net/Thanksgining/article/details/41824475?utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.controldepth_1-utm_sourcedistribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
31的信号为传统UNIX支持的信号是不可靠信号(非实时的)编号为32
63的信号是后来扩充的称做可靠信号(实时信号)。
不可靠信号和可靠信号的区别在于前者不支持排队可能会造成信号丢失而后者不会。
在用户键入INTR字符(通常是Ctrl-C)时发出用于通知前台进程组终止进程。
本信号不能被阻塞、处理和忽略。
如果管理员发现某个进程终止不了可尝试发送这个信号。
管道破裂。
这个信号通常在进程间通信产生比如采用FIFO(管道)通信的两个进程读管道没打开或者意外终止就往管道写写进程会收到SIGPIPE信号。
此外用Socket通信的两个进程写进程在写Socket的时候读进程已经终止。
与SIGKILL不同的是该信号可以被阻塞和处理。
通常用来要求程序自己正常退出shell命令kill缺省产生这个信号。
如果进程终止不了我们才会尝试SIGKILL。
如果父进程没有处理这个信号也没有等待(wait)子进程子进程虽然终止但是还会在内核进程表中占有表项这时的子进程称为僵尸进程。
这种情
况我们应该避免(父进程或者忽略SIGCHILD信号或者捕捉它或者wait它派生的子进程或者父进程先终止这时子进程的终止自动由init进程
在以上列出的信号中程序不可捕获、阻塞或忽略的信号有SIGKILL,SIGSTOP
默认会导致进程流产的信号有SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默认会导致进程停止的信号有SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有SIGCHLD,SIGPWR,SIGURG,SIGWINCH此外SIGIO在SVR4是退出在4.3BSD中是忽略SIGCONT在进程挂起时是继续否则是忽略不能被阻塞。
Ctrl-\等命令或是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号。
例如非法段存取浮点数溢出亦或是一个非法指令内核也利用信号通知进程特定事件发生。
sigqueue()、alarm()、setitimer()以及abort()。
signo)该系统调用可以用来向任何进程或进程组发送任何信号。
参数pid的值为信号的接收进程
除发送进程自身外所有进程ID大于1的进程Sinno是信号值当为0时即空信号实际不发送任何信号但照常进行错误检查因此可用于检查目标进程是否存在以及当前进程是否具有向目标发送信号的权限root权限的进程可以向任何进程发送信号非root权限的进程只能向属于同一个session或者同一个用户的进程发送信号。
Kill()最常用于pid0时的信号发送。
该调用执行成功时返回值为0错误时返回-1并设置相应的错误代码errno。
下面是一些可能返回的错误代码
ESRCH参数pid指定的进程或进程组不存在。
注意在进程表项中存在的进程可能是一个还没有被wait收回但已经终止执行的僵死进程。
进程没有权力将这个信号发送到指定接收信号的进程。
因为一个进程被允许将信号发送到进程pid时必须拥有root权力或者是发出调用的进程的UID
或EUID与指定接收的进程的UID或保存用户IDsavedset-user-ID相同。
如果参数pid小于-1即该信号发送给一个组则该错误表示组中有成员进程不能接收该信号。
3.3
进程并不一定要使用signal接收默认处理但是进程能够通过以下调用来恢复默认处理。
c给进程发信号进程收到信号后由于定义SIG_DFL所以进程会消亡。
我们已经成功完成了信号的收发那么为什么会有高级版出现呢其实之前的信号存在一个问题就是虽然发送和接收到了信号可是总感觉少些什么既然都已经把信号发送过去了为何不能再携带一些数据呢
正是如此我们需要另外的函数来通过信号传递的过程中携带一些数据。
咱么先来看看发送的函数吧。
//信号处理程序能够接受额外数据和sigqueue配合使用sigset_t
sa_mask;//阻塞关键字的信号集可以再调用捕捉函数之前把信号添加到信号阻塞字信号捕捉函数返回之前恢复为原先的值。
int
sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
};//回调函数句柄sa_handler、sa_sigaction只能任选其一signum参数指出要捕获的信号类型act参数指定新的信号处理方式oldact参数输出先前信号的处理方式如果不为NULL的话。
是一个系统调用根据这个函数原型我们不难看出在函数原型中第一个参数signum应该就是注册的信号的编号第二个参数act如果不为空说明需要对该信号有新的配置第三个参数oldact如果不为空那么可以对之前的信号配置进行备份以方便之后进行恢复。
sigaction是信号接收函数与之对应的是信号发生函数sigqueue
//如果设置了SA_SIGINFO属性说明使用的处理函数是sa_sigaction而不是sa_handler否则系统会默认使用
signum);elseprintf(error\n);if(context){printf(content:
handler;//信号处理程序能够接受额外数据和sigqueue配合使用act.sa_flags
SA_SIGINFO;//影响信号的行为SA_SIGINFO表示能够接受数据sigaction(SIGIO,
NULL);for(;;){sleep(10000);}return
sigqueue()比kill()传递了更多的附加信息但sigqueue()只能向一个进程发送信号而不能发送信号给一个进程组。
可以使用
函数不但可以发送额外的数据还可以让信号进行排队操作系统必须实现了
发送多个同一信号在解除阻塞时接受者会接收到发送的信号队列中的信号而不是直接收到一次。
但是信号不能无限的排队信号排队的最大值受到SIGQUEUE_MAX的限制达到最大限制后sigqueue
ERROR!]\n);printf(\tUsage:\n);printf(\t\t%s
V是Unix操作系统众多版本中的一支引入了三种高级进程间的通信机制消息队列、共享内存和信号量
IPC消息队列、信号量以及共享内存共享存储器之间有很多相似之处。
以上简单介绍了IPC对接下来介绍的消息队列、信号量和共享内存有助于理解。
消息队列Unix的通信机制之一可以理解为是一个存放消息数据容器。
将消息写入消息队列然后再从消息队列中取消息一般来说是先进先出的顺序。
可以解决两个进程的读写速度不同处理数据速度不同系统耦合等问题而且消息队列里的消息哪怕进程崩溃了也不会消失。
消息队列本质上是位于内核空间的链表链表的每个节点都是一条消息。
每一条消息都有自己的消息类型消息类型用整数来表示而且必须大于
的链表记录了所有消息加入队列的顺序其中红色箭头表示消息加入的顺序。
生命周期随内核消息队列会一直存在需要我们显示的调用接口删除或使用命令删除消息队列可以双向通信克服了管道只能承载无格式字节流的缺点
//类消息队列可以控制读取相应类型的数据这时就不一定是先进先出的顺序了文章后面会继续介绍
每一个消息队列都有一个对应的键值key相关联共享内存、信号量也同样需要。
path为一个已存在的路径名d为0~255之间的一个数值代表项目ID自己取
pathname所在的文件系统的信息stat结构的st_dev成员。
该文件在本文件系统内的索引节点号(stat结构的st_ino成员)。
参考https://blog.csdn.net/andylauren/article/details/78821655
flag的值为IPC_CREAT如果不存在key值的消息队列且权限不为0则创建消息队列并返回一个消息队列ID。
如果存在则直接返回消息队列ID。
flag的值为
IPC_EXCL如果不存在key值的消息队列且权限不为0则创建消息队列并返回一个消息队列ID。
如果存在则产生错误。
msgget(key,IPC_CREAT|IPC_EXCL|0666);创建一个权限为0666所有用户可读可写具体查询linux权限相关内容的消息队列并返回一个整形消息队列ID如果key值已经存在有消息队列了则出错返回-1。
msgget(key,IPC_CREAT|0666);创建一个权限为0666所有用户可读可写具体查询linux权限相关内容的消息队列并返回一个消息队列ID如果key值已经存在有消息队列了则直接返回一个消息队列ID。
nbytes:为消息结构体mymesg里的字符数组mtext大小sizeof(mtext)
为0时当消息队列满时msgsnd将会阻塞直到消息能写进消息队列或者消息队列被删除。
为IPC_NOWAIT时当消息队列满了msgsnd函数将不会等待会立即出错返回EAGAIN
nbytes:为消息结构体mymesg里的字符数组mtext大小sizeof(mtext)type:在结构体mymesg里我们定义了一个long
返回队列中消息类型值小于等于type绝对值的消息如果这种消息有若干个则取类型值最小的消息flag:可以为0、IPC_NOWAIT、IPC_EXCEPT为0时阻塞式接收消息没有该类型的消息msgrcv函数一直阻塞等待为IPC_NOWAIT时如果没有返回条件的消息调用立即返回此时错误码为ENOMSG为IPC_EXCEPT时与msgtype配合使用返回队列中第一个类型不为msgtype的消息返回值成功返回消息数据部分的长度错误返回-1
msgid就是msgget函数返回的消息队列IDcmd有三个常用删除消息队列的为IPC_RMIDIPC_STAT取此队列的msqid_ds结构并将它存放在buf指向的结构中IPC_SET改变消息队列的状态把buf所指的msqid_ds结构中的uid、gid、mode复制到消息队列的msqid_ds结构内。
内核为每个消息队列维护着一个结构结构名为msqid_ds这里就不讲啦里面存放着消息队列的大小pid存放时间等一些参数buf就是结构体msqid_ds返回值成功返回0错误返回-1
例如msgctl(id,IPC_RMID,NULL);删除id号的消息队列
下面为一个简单的程序一个service和一个clientservice往消息队列里写数据client从消息队列里读数据当service输入QUIT时删除消息队列并且俩程序都退出。
msg[512];memset(msg,0,sizeof(msg));ckxmsg.mtype
message:);fgets(msg,sizeof(msg),stdin);strcpy(ckxmsg.mtext,msg);if(msgsnd(id,(void
0)break;}if(msgctl(id,IPC_RMID,NULL)
msgget(key,0666|IPC_CREAT);if(id
0;}printf(data:%s\n,ckxmsg.mtext);if(strncmp(ckxmsg.mtext,QUIT,4)
参考https://blog.csdn.net/qinxiongxu/article/details/7830537
信号量的使用主要是用来保护共享资源使得资源在一个时刻只有一个进程线程
信号量的值为正的时候说明它空闲。
所测试的线程可以锁定而使用它。
若为0说明
信号量广泛用于进程或线程间的同步和互斥同步是一种更为复杂的互斥而互斥是一种特殊的同步同步互斥的定义参考https://blog.csdn.net/liming0931/article/details/82902084
用户态进程使用的信号量这种信号量又分为POSIX信号量和SYSTEM
所以它可以用于线程也可以用于进程间的同步。
无名信号量其值保存在内存中。
倘若对信号量没有以上的全面认识的话你就会很快发现自己在信号量的森林里迷失了方向。
参考http://blog.chinaunix.net/uid-24219701-id-3286606.html
V的IPC机制信号量是相同的不过他绝不可能在内核之外使用因此他和System
内核信号量类似于自旋锁因为当锁关闭着时它不允许内核控制路径继续进行。
然而
当内核控制路径试图获取内核信号量锁保护的忙资源时相应的进程就被挂起。
只有在资源
semaphore类型的对象它在asm/semaphore.h中定义
wait;}count相当于信号量的值大于0资源空闲等于0资源忙但没有进程等待这
wait存放等待队列链表的地址当前等待资源的所有睡眠进程都会放在这个链表中。
sleepers存放一个标志表示是否有一些进程在信号量上睡眠。
上面已经提到了内核信号量使用了等待队列wait_queue来实现阻塞操作。
当某任务由于没有某种条件没有得到满足时它就被挂到等待队列中睡眠。
当条件得到满足
时该任务就被移出等待队列此时并不意味着该任务就被马上执行因为它又被移进工
内核信号量是在内部使用等待队列的也就是说该等待队列对用户是隐藏的无须用
在2.6.36版本后的内核就没有DECLARE_MUTEX这个宏了取而代之的是DEFINE_SEMAPHORE宏在后来同互斥信号量相关的init_MUTEX、init_MUTEX_LOCKED也从linux/semaphore.h文件中移除了。
__DECLARE_SEMAPHORE_GENERIC(name,1)
IPC服务的信号量只不过是它的一部分。
常用于进程间同步。
POSIX信号量的引用头文件是“semaphore.h”而SYSTEM
参考https://blog.csdn.net/qq_35433716/article/details/86382733
标准中信号量分两种一种是无名信号量一种是有名信号量。
无名信号量一般用于线程间同步或互斥而有名信号量一般用于进程间同步或互斥。
它们的区别和管道及命名管道的区别类似无名信号量则直接保存在内存中而有名信号量要求创建一个文件。
{sem_wait(sem);//减一while(*str){putchar(*str);
fflush(stdout);str;sleep(1);}printf(\n);sem_post(sem);//加一}void
NULL);//等待线程结束回收其资源pthread_join(tid1,
{while(1){sem_wait(sem_g);ch;sleep(1);sem_post(sem_p);}}void
{while(1){sem_wait(sem_p);printf(%c,ch);fflush(stdout);sem_post(sem_g);}}int
name信号量文件名。
注意不能指定路径名。
因为有名信号量默认放在/dev/shm
//信号量减一while(*str!\0){putchar(*str);
fflush(stdout);str;sleep(1);}printf(\n);
//子进程//跟open()打开方式很相似,不同进程只要名字一样那么打开的就是同一个有名信号量sem
//有名信号量创建失败perror(sem_open);return
//父进程//跟open()打开方式很相似,不同进程只要名字一样那么打开的就是同一个有名信号量sem
SEM_FAILED){//有名信号量创建失败perror(sem_open);return
//等待子进程结束}sem_unlink(name_sem);//删除有名信号量return
0;while(1){sem_wait(print1);i;printf(int
print1){perror(sem_open);}print2
print2){perror(sem_open);}print(print1,
0;while(1){sem_wait(print2);i;printf(in
i);sleep(1);sem_post(print1);}}int
print1){perror(sem_open);}print2
print2){perror(sem_open);}print(print1,
//删除信号量文件sem_print1sem_del(sem_print2);
参考https://blog.csdn.net/qq_35433716/article/details/86260653
共享内存是进程间通信中最简单的方式之一。
共享内存允许两个或更多进程访问同一块内存就如同
函数向不同进程返回了指向同一个物理内存区域的指针。
当一个进程改变了这块地址中的内容的时候其它进程都会察觉到这个更改。
一个进程向共享的内存区域写入了数据共享这个内存区域的所有进程就可以立刻看到其中的内容。
2使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。
若一个进程正在向共享内存区写数据则在它做完这一步操作前别的进程不应当去读、写这些数据。
位或权限位共享内存位或权限位后可以设置共享内存的访问权限格式和
-1){perror(ftok);}//创建共享内存shmid
用于锁定内存禁止内存交换。
并不代表共享内存被锁定后禁止其它进程访问。
其真正的意义是被锁定的内存不允许被交换到虚拟内存中。
这样做的优势在于让共享内存一直处于内存中从而提高程序性能
-1){perror(ftok);}//创建共享内存shmid
0){perror(shmat);_exit(-1);}//拷贝数据至共享内存区printf(copy
0){perror(shmat);exit(-1);}//读共享内存区数据printf(data
0){perror(shmdt);exit(1);}else{printf(deleted
shared-memory\n);}//删除共享内存shmctl(shmid,
参考https://blog.csdn.net/baobao1767640830/article/details/106200463
1IP协议有两个版本IPv4和IPv6现在用得比较多的是IPv4。
4通常使用“点分十进制”的字符串表示IP地址例如“192.168.181.129”其中用点分割的每一个数字表示一个字节范围为0-255。
5在IP数据报头部有两个IP地址分别叫做原IP地址和目的IP地址。
IP地址能够把信息发送到对方的机器上但是还需要一个其他的标识符来区分这个数据要给哪个程序进行解析它就是端口号。
2端口号用来标识一个进程告诉操作系统当前的这个数据要交给哪一个进程来处理。
3端口号是一个16位的整数可以标识的范围是0-65535。
其中0-1023是公认端口号即已经公认定义或为将要公认定义的软件保留的而1024-65535是并没有公共定义的端口号用户可以自己定义这些端口的作用。
5一个进程能够占用多个端口号但是一个端口号只能被一个进程占用理解
socket也是一种进程间的通信机制不过它与其他通信方式主要的区别是它可以实现不同主机间的进程通信。
套接字连接的过程如同客户打一个电话到一个大公司接线员服务器进程接听电话并把它转接到你要找的部门然后再从那里转到你要找的人服务器套接字然后接线员服务器进程再继续转接其它客户的电话。
套接字给予进程在相同设备主机上基于通道的通信能力而网络套接字给予进程运行在不同主机的能力因此也带来了网络通信的能力。
网络套接字需要底层协议的支持例如
通信使用一个本地的文件作为套接字地址。
尽管这两种套接字的实现有所不同但在本质上IPC
套接字接口是一组函数用以创建网络应用,存放在“sys/socket.h”函数库中。
protocoldomain地址域版本号IPV4。
通常使用AF_INET表示32位IP地址AF_INET是网络套接字AF_UNIX是本地套接字
type套接字类型分为两种。
SOCK_STREAM表示字节流类型TCPSOCK_DGRAM表示数据报类型UDP
protocol:协议。
存在三种形式IPPROTO_TCP表示TCP协议IPPROTO_UDP表示UDP协议“0”表示接受任何协议。
socket函数目的是打开一个网络通讯端口如果成功就像open一样返回一个文件描述符应用程序可以像读写文件一样用read/write在网络上收发数据。
addlenIPv4结构体的大小即sizeof(sockaddr_in)
sizeof(serveraddr));Serveraddr.sin_family
htons(SERV_PORT);Serveraddr.sin_addr.s_addr
htonl(INADDR_ANY);1)bzero表示将整个结构体清零。
2)网络地址为INADDR_ANY表示本地的任意IP地址。
因为服务器可能有多个网卡每个网卡也可能绑定多个IP地址这样设置可以在所有的IP地址上监听直到某个客户端建立了连接释才确定下来到底用哪个IP地址。
a.服务器程序所监听的网络端口号和网络地址通常是固定不变客户端得知服务器程序的地址和端口号后就可以向服务器发起连接请求。
所以服务器需要绑定一个固定的网络地址和端口号。
sockaddr*”是通用指针类型实际上serveraddr参数可以接受多种协议的sockaddr结构体所以需要第三个参数addrlen指定结构体的长度
c.客户端不是不允许调用bind函数只是没有必要调用bind函数固定一个端口。
否则如果在同一台机器上启动多个客户端就会出现端口号被占用导致不能正确建立连接。
d.服务器也不是必须调用bind函数但是如果服务器不调用bind函数内核就会自动给服务器分配监听端口每次启动服务器时端口号都不一样客户端要连接服务器就会遇到麻烦。
函数功能服务器告知内核套接字被服务器使用了转化为监听套接字接受客户端的连接请求。
listen函数声明sockfd处于监听状态并且最多允许有backlog个客户端处于连接等待状态如果接收到更多的连接请求就忽略。
一般backlog的值不会太大backlog的设置主要是为了提高客户端与服务器的连接效率太大了会消耗很多内存得不偿失。
函数功能客户端通过调用connect函数来建立和服务器的连接。
addrlenIPv4结构体的大小即sizeof(struct
onnect函数和bind函数的参数形式一样区别在于bind的参数是自己的地址而connect的参数是对方的地址
函数功能服务器通过调用accept函数接收来自客户端的连接请求。
2.服务器如果调用accept函数时还没有客户端的连接请求就阻塞等待直到客户端连接上来。
3.addr是一个传出参数accept函数返回时传出客户端的地址和端口号。
5.addrlen参数是一个传入传出参数传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出的问题传出的是客户端地址结构的实际长度。
sockaddr结构是套接字的总体结构包括16位的地址类型和14字节的地址数据如下图所示。
问题在connect、bind和accept函数中要求一个指向与协议相关的的套接字地址结构的指针。
如何能接受各种类型的套接字地址结构
解决办法定义套接字函数要求一个指向通用sockaddr结构的指针然后要求应用程序将与协议特定的结构的指针强制转换成这个通用的指针结构。
IPv4地址用sockaddr_in结构体表示包括16位的地址类型、16位的端口号和32位的IP地址其中IPv4的地址用AF_INET表示。
是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
内存中的多字节数据对于内存地址有大端小端之分磁盘文件中的多字节数据对于文件偏移量有大端小端之分网络数据流同样有大端小端之分。
为使网络程序具有可移植性使同样的C代码在大端和小端计算机上编译后能正常运行可以调用以下库函数做网络字节序和主机字节序的转换它们存在arpa/inet.h头文件中。
h表示hostn表示networkl表示32位长整数s表示16位短整数to表示转换。
即htonl表示将32位整数由主机字节序转换为网络字节序返回网络字节序的值ntohl表示将32位整数由网络字节序转换为主机字节序返回主机字节序的值。
htons和ntohs函数为16位无符号整数执行相应的转换
网络字节顺序的IP地址是二进制的数据为了方便使用需要转换为点分十进制的字符串。
例如128.2.194.242就是地址0x8002c2f2的点分十进制表示。
应用程序可以使用以下库函数实现IP地址与点分十进制串的转换它们存放在arpa/inet.h头文件中。
该函数将一个点分十进制串转换为一个二进制的网络字节顺序的IP地址。
如果src没有指向一个合法的点分十进制字符串那么该函数返回0。
成功返回1失败返回-1。
该函数将一个二进制的网络字节顺序的IP地址转换为它对应的点分十进制的字符串并把得到的以null结尾的字符串复制到dst。
成功返回指向点分十进制的指针失败返回NULL
client_socket;bzero(server_socket,
sizeof(server_socket));server_socket.sin_family
htons(_PORT_);server_socket.sin_addr.s_addr
htonl(INADDR_ANY);if(bind(sock,
strerror(errno));close(sock);return
strerror(errno));close(sock);return
strerror(errno));close(sock);return
sizeof(buf_ip));inet_ntop(AF_INET,
sizeof(buf_ip));//存放客户端套接字的地址printf(get
ntohs(client_socket.sin_port));while(1){char
wait...\n);}}close(sock);return
sizeof(server_sock));server_sock.sin_family
server_sock.sin_addr);server_sock.sin_port
success...\n);while(1){printf(client
0){printf(quit!\n);break;}printf(please
若再启动一个客户端尝试连接服务器发现第二个客户端无法与服务器连接成功。
因为调用accept接受一个请求之后就在while循环里一直尝试read没有调用accept函数接受客户端的连接请求而导致连接失败。
注意理解接收请求时先由父进程创建子进程再由子进程创建孙子进程然后由孙子进程来处理与客户端的交互为什么
答假设不创建孙子进程由子进程处理与客户端的交互这时父进程一直在等待子进程的退出而不执行它下面的代码显然是不行的。
创建了孙子进程由孙子进程处理与客户端的交互子进程退出父进程回收子进程孙子进程被init进程领养。
(Arg*)ptr;ProcessRequest(arg-fd,
error!\n);continue;}if(read_size
inet_ntoa(client_addr-sin_addr));close(client_fd);break;}buf[read_size]
inet_ntoa(client_addr-sin_addr),
htons(atoi(argv[2]));addr.sin_addr.s_addr
(Arg*)malloc(sizeof(Arg));arg-fd
client_addr;pthread_create(tid,
(void*)arg);pthread_detach(tid);}return
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback