《UNIX网络编程 卷2》读书笔记(一)

1,获取Posix IPC的名字

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

None.gif#include"unpipc.h"
None.gif
None.gif
char*px_ipc_name(constchar*name)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
char*dir,*dst,*slash;
InBlock.gif
if((dst=malloc(PATH_MAX))==NULL)returnNULL;//分配失败
InBlock.gif
if((dir=getenv("PX_IPC_NAME"))==NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//目录名
InBlock.gif
#ifdefPOSIX_IPC_PREFIX
InBlock.gifdir
=POSIX_IPC_PREFIX;
InBlock.gif
#else
InBlock.gifdir
="/tmp/";
InBlock.gif
#endif
ExpandedSubBlockEnd.gif}

InBlock.gifslash
=(dir[strlen(dir)-1]=='/')?"":"/";
InBlock.gifsnprintf(dst,PATH_MAX,
"%S%S%S",dir,slash,name);//全路径名称
InBlock.gif
returndst;
ExpandedBlockEnd.gif}

None.gif

2Mq_open,lsem_open,shm_open用来创建或打开一个IPC对象,第2个参数oflag指定打开IPC对象的方式。消息队列可以以只读,只写或读写任何一种模式打开,信号灯的打开不指定任何模式,共享内存区不能以只写模式打开

3System V IPC使用key_t作为其名字,通常是先调用stat函数,再调用ftok函数得到的,函数ftok把一个已经存在的路径和一个整数标识符转换为一个key_t,IPC(IPC key).

4,内核给每个IPC对象维护一个数据结构

None.gifstructipc_perm
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gifuid_tuid;
//owner的用户ID
InBlock.gif
gid_tgid;//owner的组ID
InBlock.gif
uid_tcuid;//creater的用户ID
InBlock.gif
gid_tcgid;//creater的组ID
InBlock.gif
mode_tmode;//读写模式
InBlock.gif
ulong_tseq;//序列号
InBlock.gif
key_tkey;//IPCkey
ExpandedBlockEnd.gif
}
;
None.gif

ipc_perm 结构体中的seq成员很有意思,它是作为一个计数器,每当删除一个IPC时,内核就递增这个变量,若溢出则循环回到0.

5,管道是最初的Unix IPC形式,其局限性在于没有名字,而且只能由有亲缘关系的进程使用。FIFO又叫有名管道,任何进程之间都能使用。两者都可以使用通常的readwrite访问。

6,

None.gif#include"unpipc.h"
None.gif
voidclient(intreadfd,intwritefd)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//客户
InBlock.gif
size_tlen;
InBlock.gifssize_tn;
InBlock.gif
charbuff[MAXLINE];
InBlock.giffgets(buff,MAXLINE,stdin);
//读入路径名
InBlock.gif
len=strlen(buff);
InBlock.gif
if(buff[len-1]=='/n')//去掉结尾处的行符
InBlock.gif
len--;
InBlock.gifwrite(writefd,buff,len);
//写入管道
InBlock.gif
while((n==read(readfd,buff,MAXLINE))>0)//从管道读入数据
InBlock.gif
write(STDOU_FILENO,buff,n);//输出
InBlock.gif

ExpandedBlockEnd.gif}

None.gif
voidserver(intreadfd,intwritefd)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//服务器
InBlock.gif
intfd;
InBlock.gifssize_tn;
InBlock.gif
charbuff[MAXLINE+1];
InBlock.gif
InBlock.gif
if((n==read(readfd,buff,MAXLINE))>0)//从管道读入数据
InBlock.gif
buff[n]='/0';
InBlock.gif
if((fd=open(buff,0_RDONLY))<0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//给客户端返回出错信息
InBlock.gif
snprintf(buff+n,sizeof(buff)-n,"can'topen,%s/n",strerror(errno));
InBlock.gifn
=strlen(buff);
InBlock.gifwrite(writefd,buff,n);
ExpandedSubBlockEnd.gif}

InBlock.gif
else
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
while((n=read(fd,huff,MAXLINE))>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//给客户端返回文件内容
InBlock.gif
write(writefd,buff,n);
ExpandedSubBlockEnd.gif}

InBlock.gifclose(fd);
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif
None.gif
intmain(intargc,char**argv)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
intpipe1[2],pipe2[2];
InBlock.gifpid_tchildpid;
InBlock.gif
//创建两个管道
InBlock.gif
pipe(pipe1);
InBlock.gifpipe(pipe2);
InBlock.gif
if((childpid=fork())==0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifclose(pipe1[
1]);//子进程关闭管道1的写入端
InBlock.gif
close(pipe2[0]);//子进程关闭管道2的读出端
InBlock.gif
server(pipe1[0],pipe2[1]);
InBlock.gifexit(
0);
ExpandedSubBlockEnd.gif}

InBlock.gifclose(pipe1[
0]);//父进程关闭管道1的读出端
InBlock.gif
close(pipe2[1]);//父进程关闭管道2的写入端
InBlock.gif
client(pipe2[0],pipe1[1]);
InBlock.gifwaitpid(childpid,NULL,
0);
InBlock.gif
return0;
ExpandedBlockEnd.gif}

None.gif
None.gif


上面这个例子也可以使用popencat来实现

None.gif#include"unpipc.h"
None.gif
None.gif
intmain(intargc,char**argv)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gifsize_tn;
InBlock.gif
charbuff[MAXLINE],command[MAXLINE];
InBlock.gifFILE
*fp;
InBlock.giffgets(buff,MAXLINE,stdin);
//读入路径名
InBlock.gif
n=strlen(buff);
InBlock.gif
if(buff[n-1]=='/n')n--;
InBlock.gifsnprintf(command,
sizeof(command),"cat%s",buff);//构造命令字
InBlock.gif
fp=popen(command,"r");//创建管道并启动另一个进程
InBlock.gif
while(fgets(buff,MAXLINE,fp)!=NULL)
InBlock.giffputs(buff,stdout);
InBlock.gifpclose(fp);
InBlock.gif
return0;
ExpandedBlockEnd.gif}

None.gif

7FIFO是一个单向数据流,有一个路径名与之关联,从而允许没有亲缘关系的进程访问同一个FIFO,也叫有名管道(named pipe),由函数mkfifo创建。

None.gif#include"unpipc.h"
None.gif
#defineFIFO1"tmp/fifo.1"
None.gif
#defineFIFO2"tmp/fifo.2"
None.gif
None.gif
voidserver(intreadfd,intwritefd)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//服务器
InBlock.gif
intfd;
InBlock.gifssize_tn;
InBlock.gif
charbuff[MAXLINE+1];
InBlock.gif
InBlock.gif
if((n==read(readfd,buff,MAXLINE))>0)//从管道读入数据
InBlock.gif
buff[n]='/0';
InBlock.gif
if((fd=open(buff,0_RDONLY))<0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//给客户端返回出错信息
InBlock.gif
snprintf(buff+n,sizeof(buff)-n,"can'topen,%s/n",strerror(errno));
InBlock.gifn
=strlen(buff);
InBlock.gifwrite(writefd,buff,n);
ExpandedSubBlockEnd.gif}

InBlock.gif
else
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
while((n=read(fd,huff,MAXLINE))>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//给客户端返回文件内容
InBlock.gif
write(writefd,buff,n);
ExpandedSubBlockEnd.gif}

InBlock.gifclose(fd);
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif
intmain(intargc,char**argv)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
intreadfd,writefd;
InBlock.gifpid_tchildpid;
InBlock.gif
//创建两个有名管道
InBlock.gif
mkfifo(FIFO1,FILE_MODE);
InBlock.gifmkfifo(FIFO2,FILE_MODE);
InBlock.gif
InBlock.gifreadfd
=open(FIFO1,O_RDONLY,0);
InBlock.gifwritefd
=open(FIFO2,O_WRONLY,0);
InBlock.gifserver(readfd,writefd);
InBlock.gif
return0;
ExpandedBlockEnd.gif}

None.gif
None.gif
voidclient(intreadfd,intwritefd)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//客户
InBlock.gif
size_tlen;
InBlock.gifssize_tn;
InBlock.gif
charbuff[MAXLINE];
InBlock.giffgets(buff,MAXLINE,stdin);
//读入路径名
InBlock.gif
len=strlen(buff);
InBlock.gif
if(buff[len-1]=='/n')//去掉结尾处的行符
InBlock.gif
len--;
InBlock.gifwrite(writefd,buff,len);
//写入管道
InBlock.gif
while((n==read(readfd,buff,MAXLINE))>0)//从管道读入数据
InBlock.gif
write(STDOU_FILENO,buff,n);//输出
InBlock.gif

ExpandedBlockEnd.gif}

None.gif
None.gif
intmain(intargc,char**argv)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
intreadfd,writefd;
InBlock.gifpid_tchildpid;
InBlock.gif
//创建两个有名管道
InBlock.gif
mkfifo(FIFO1,FILE_MODE);
InBlock.gifmkfifo(FIFO2,FILE_MODE);
InBlock.gif
InBlock.gifreadfd
=open(FIFO1,O_RDONLY,0);
InBlock.gifwritefd
=open(FIFO2,O_WRONLY,0);
InBlock.gifserver(readfd,writefd);
InBlock.gif
InBlock.gifwritefd
=open(FIFO1,O_WRONLY,0);
InBlock.gifreadfd
=open(FIFO2,O_RDONLY,0);
InBlock.gifclient(readfd,writefd);
InBlock.gif
InBlock.gifclose(readfd);
InBlock.gifclose(writefd);
InBlock.gif
//删除有名管道
InBlock.gif
unlink(FIFO1);
InBlock.gifunlink(FIFO2);
InBlock.gif
return0;
ExpandedBlockEnd.gif}

None.gif

8,能通过两种方式设置为非阻塞:

1)调用open时指定O_NONBLOCK标志。

2)若描述字已经打开,用fcntlenableO_NONBLOCK标志,对于管道来说,必须这样,因为它没有open调用,在pipe调用中也无法指定O_NONBLOCK标志,代码如下:

None.gifintflags;
None.gifflags
=fcntl(fd,F_GETFL,0);
None.gifflags
|=O_NONBLOCK;
None.giffcntl(fd,F_SETFL,flags);

9,单进程服务器,多客户端

服务器代码:

None.gifintmain(intargc,char**argv)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
intreadfifo,writefifo,dummyfd,fd;
InBlock.gif
char*ptr,buff[MAXLINE],fifoname[MAXLINE];
InBlock.gifpid_tpid;
InBlock.gifssize_tn;
InBlock.gifmkfifo(SERV_FIFO,FILE_MODE);
InBlock.gifreadfifo
=open(SERV_FIFO,O_RDONLY,0);//服务器的读管道
InBlock.gif
dummyfd=open(SERV_FIFO,O_WRONLY,0);//写管道,没使用,开启为了防止服务器管道重复打开,关闭
InBlock.gif
while((n=readline(readfifo,buff,MAXLINE))>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
if(buff[n-1]=='/n')n--;
InBlock.gifbuff[n]
='/0';
InBlock.gif
if((ptr=strchr(buff,''))==NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.giferr_msg(
"请求命令错误");
InBlock.gif
continue;
ExpandedSubBlockEnd.gif}

InBlock.gif
*ptr++=0;
InBlock.gifpid
=atoi(buff);//获取客户端进程号
InBlock.gif
snprintf(fifoname,sizeof(fifoname),"/tmp/fifo.%ld",(long)pid);
InBlock.gifwritefifo
=open(fifoname,O_WRONLY,0);//打开客户端管道的写端
InBlock.gif
if(fd=open(ptr,O_RDONLY,0))<0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//读文件失败
InBlock.gif
snprintf(buff+n,sizeof(buff)-n,"can'topen,%s/n",strerror(errno));
InBlock.gifn
=strlen(ptr);
InBlock.gifwrite(writefifo,ptr,n);
//往客户端管道写数据
InBlock.gif
close(writefifo);
ExpandedSubBlockEnd.gif}

InBlock.gif
else
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
while((n=read(fd,buff,MAXLINE))>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifwrite(writefifo,buff,n);
ExpandedSubBlockEnd.gif}

InBlock.gifclose(fd);
InBlock.gifclose(writefifo);
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

InBlock.gif
return0;
ExpandedBlockEnd.gif}

None.gif

客户端代码:

None.gifintmain(intargc,char**argv)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
intreadfifo,writefifo;
InBlock.gifsize_tlen;
InBlock.gifssize_tn;
InBlock.gif
char*ptr,buff[MAXLINE],fifoname[MAXLINE];
InBlock.gifpid_tpid;
InBlock.gifpid
=getpid();//获取当前进程ID
InBlock.gif
snprintf(fifoname,sizeof(fifoname),"/tmp/fifo.%ld",(long)pid);
InBlock.gif
InBlock.gifmkfifo(fifoname,FILE_MODE);
//创建客户端管道
InBlock.gif
snprintf(buff,sizeof(buff),"%ld",(long)pid);
InBlock.giflen
=strlen(buff);
InBlock.gifptr
=buff+len;
InBlock.gif
//读入文件路径名
InBlock.gif
fgets(ptr,MAXLINE-len,stdin);
InBlock.giflen
=strlen(buff);
InBlock.gifwritefifo
=open(SERV_FIFO,O_WRONLY,0);//打开服务器的写管道/
InBlock.gif
write(writefifo,buff,len);//给服务器传送指定格式的命令字
InBlock.gif
readfifo=open(fifoname,O_RDONLY,0);/打开客户端读管道
InBlock.gif
InBlock.gif
while((n=read(readfifo,buff,MAXLINE))>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifwrite(STDOUT_FILENO,buff,n);
//输出
ExpandedSubBlockEnd.gif
}

InBlock.gifclose(readfifo);
//关闭读端
InBlock.gif
unlink(fifoname);//删除管道
InBlock.gif
return0;
ExpandedBlockEnd.gif}

None.gif

但是这个程序会导致DoS攻击,因为服务器会一直阻塞在对客户端FIFOopen调用中,若客户端不打开此FIFO来读,则服务器会一直阻塞,因此客户很容易通过阻止发送封包来使得服务器垮掉,解决办法有很多,可以一个客户一个进程,或一个客户一个线程,或使用进程池,或线程池。

10,UNIX默认的I/O模型是字节流模型,也就是不存在记录边界,一般有3种技巧来改造:

1)带内特殊终止序列:很多使用换行符来分隔每个消息,写入进程给每个消息加入一个换行符,读出进程每次读出一行,但缺点是分隔符要进行转义处理。如:FTP,SMTP等就使用一个回车后跟一个换行符来分隔记录。

2)显示长度:记录前加入其长度。

3)一次连接一个记录,通过关闭与对方的连接来指示一个记录结束,例如HTTP1.0

None.gif#include"unpipc.h"
None.gif
None.gif
#defineMAXMESGDATA(PIPE_BUF-2*sizeof(long))
None.gif
#defineMESGHDRSIZE(sizeof(structmymesg)-MAXMESGDATA)
None.gif
None.gif
structmymesg
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
longmesg_len;//消息长度
InBlock.gif
longmesg_type;//消息类型
InBlock.gif
longmesg_data[MAXMESGDATA];//数据域
ExpandedBlockEnd.gif
}
;
None.gif
None.gifsize_tmesg_send(
intfd,structmymesg*mptr)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
return(write(fd,mptr,MESGHDRSIZE+mptr->mesg_len));
ExpandedBlockEnd.gif}

None.gif
None.gifssize_tmesg_recv(
intfd,structmymesg*mptr)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gifsize_tlen;
InBlock.gifssize_tn;
InBlock.gif
if((n=read(fd,mptr,MESGHDRSIZE))==0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
return0;
ExpandedSubBlockEnd.gif}

InBlock.gif
elseif(n!=MESGHDRSIZE)
InBlock.giferr_quit(
"头部错误");
InBlock.gif
if((len=mptr->mesg_len)>0)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
if((n=read(fd,mptr->mesg_data,len))!=len)
InBlock.giferr_quit(
"数据错误");
ExpandedSubBlockEnd.gif}

InBlock.gif
returnlen;
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
voidclient(intreadfd,intwritefd)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//客户
InBlock.gif
size_tlen;
InBlock.gifssize_tn;
InBlock.gif
structmymesgmesg;
InBlock.gif
InBlock.gif
charbuff[MAXLINE];
InBlock.giffgets(mesg.mesg_data,MAXMESGDATA,stdin);
//读入路径名
InBlock.gif
len=strlen(mesg.mesg_data);
InBlock.gif
if(mesg.mesg_data[len-1]=='/n')//去掉结尾处的行符
InBlock.gif
len--;
InBlock.gifmesg.mesg_len
=len;
InBlock.gifmesg.mesg_type
=1;
InBlock.gifmesg_send(writefd,
&mesg);
InBlock.gif
while((n==mesg_recv(readfd,&mesg))>0)//从管道读入数据
InBlock.gif
write(STDOU_FILENO,mesg.mesg_data,n);//输出
InBlock.gif

ExpandedBlockEnd.gif}

None.gif
voidserver(intreadfd,intwritefd)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{//服务器
InBlock.gif
intfd;
InBlock.gifssize_tn;
InBlock.gif
charbuff[MAXLINE+1];
InBlock.gifFIFL
*fp;
InBlock.gif
structmymesgmesg;
InBlock.gifmesg.mesg_type
=1;
InBlock.gif
InBlock.gif
InBlock.gif
if((n==mesg_recv(readfd,&mesg))>0)//从管道读入数据
InBlock.gif
mesg.mesg_data[n]='/0';
InBlock.gif
if((fd=fopen(mesg.mesg_data,"r"))==NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//给客户端返回出错信息
InBlock.gif
snprintf(mesg.mesg_data+n,sizeof(mesg.mesg_data)-n,"can'topen,%s/n",strerror(errno));
InBlock.gifmesg.mesg_len
=strlen(mesg.mesg_data);
InBlock.gifmesg_send(writefd,
&mesg);
ExpandedSubBlockEnd.gif}

InBlock.gif
else
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
while(fgets(mesg.mesg_data,MAXMESGDATA,fp))!=NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{//给客户端返回文件内容
InBlock.gif
mesg.mesg_len=strlen(mesg.mesg_data);
InBlock.gifmesg_send(writefd,
&mesg);
ExpandedSubBlockEnd.gif}

InBlock.giffclose(fp);
ExpandedSubBlockEnd.gif}

InBlock.gifmesg.mesg_len
=0;
InBlock.gifmesg_send(writefd,
&mesg);
ExpandedBlockEnd.gif}

None.gif
阅读更多

没有更多推荐了,返回首页