《网络应用程序设计》课件第5章 进程间通信及实现方法_第1页
《网络应用程序设计》课件第5章 进程间通信及实现方法_第2页
《网络应用程序设计》课件第5章 进程间通信及实现方法_第3页
《网络应用程序设计》课件第5章 进程间通信及实现方法_第4页
《网络应用程序设计》课件第5章 进程间通信及实现方法_第5页
已阅读5页,还剩90页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第5章进程间通信及实现方法

5.1管道

5.2命名管道5.3消息通信5.4共享内存5.5信号灯5.6UNIX域套接字习题5.1管道 1.管道的定义

Linux系统提供了几种通信方式,如消息队列、信号量及共享内存等。它们有的需要使用较多的存储资源才能进行信息传递,有的不适合在进程间进行大量的信息传递。而管道机构能够为进程之间大量信息的传输提供通道。管道是Linux系统利用文件系统为基础构成的一种进程间通信的机构。它有以下几个特点:

(1)管道是一个单向的通信信道,发送进程能以比较简单的方式,把要发送的信息以信息流的方式写入信道,不需要考虑对每次传送信息长度的限制。 (2)接收进程从信道按需取用信息,不必考虑长度限制。

(3)发送和接收进程的实施相互协调,其中发送和接收进程相互协调指的是: ①

发送和接收进程对通信信道的使用是互斥的,一个进程利用信道进行读或写操作时,其他进程就不能用,当这个进程不用信道时,其他的进程才能使用。 ②

发送和接收进程都能了解对方是否存在。 ③

发送和接收进程同步。即接收进程在信道中已有信息时,才接收信息;发送进程要在信道空闲时,才能发送信息。

管道是由系统调用pipe建立的,其调用格式为:

#include<unistd.h> intpipe(intfd[2]);

参数fd指向两个文件描述符;fd[0]返回管道读通道打开的文件号,用于读;fd[1]返回管道的写通道打开的文件号,用于写,如图5-1所示。图5-1管道工作示意图 2.管道的共享使用

进程利用pipe系统调用生成管道后,通常接着就要创建一个或几个子进程,管道被父子进程共享。每个进程可以用类似文件读写操作方式对管道进行存取操作。一般情况下,一个管道最好为两个进程专用,一个进程只用其发送端,另一个进程只用其接收端。

当需要进行双向数据传输时,必须建立两个管道。每个管道负责一个方向的数据传输。它的操作过程如下:

●创建两个管道:管道1和管道2;

●调用fork()产生子进程;

●父进程关闭管道1的读端; ●父进程关闭管道2的写端;

●子进程关闭管道1的写端; ●子进程关闭管道2的读端。 下面的例子说明了管道机构的建立和使用。

main() { charparstr[]={"thedatasfromparent.\n"};

charchistr[]={"thedatasfromchild.\n"};

intchpipe1[2],

chpipe2[2];

charbuff[100]; if(pipe(chpipe1)<0)err_sys("pipe1createrror");

if(pipe(chpipe2)<0)err_sys("pipe2createrror");

if(fork()){/*在父进程中

*/close(chpipe1[0]);

close(chpipe2[1]); if(write(chpipe1[1],parstr,

sizeof(parstr))!=sizeof(parstr))err_sys("senderror");

close(chpipe1[1]);

if(read(chpipe2[0],buff,100)<=0)err_sys("receiveerror");

elseprintf("parentprocess:%s\n",buff);

}/*fork*/

else{close(chpipe1[1]);

/*

在子进程中

*/close(chpipe2[0]);

if(read(chpipe1[0],buff,100)<=0)err_sys("receiveerror");

elseprintf("childprocess:%s\n",buff);

if(write(chpipe2[1],chistr,sizeof(chistr))!=sizeof(chistr))err_sys("senderror");

close(chpipe2[1]);

}}

运行结果

childprocess:

thedatasfromparent. parentprocess:

thedatasfromchild.

程序中信息的传输过程如图5-2所示。图5-2利用管道进行通信

首先,进程创建两个管道chpipe1和chpipe2,然后利用

fork产生一子进程。如果子进程创建成功,父进程利用管道chpipe1[1]作写入端,用管道chpipe2[0]作读出端,关闭管道端chpipe1[0]和chpipe2[1]。子进程用管道chpipe1[0]作为读出端,用管道chpipe2[1]作为写入端,关闭管道端chpipe1[1]和chpipe2[0],两者进行通信。就每个管道而言,均是单向管道,其数据信息只按一个方向流动。就每个进程而言,信息的传送是双向的,即能发送又能接收。 如果将上面程序改为在顾客—服务员模式上运行,上例程序可改写为:由main()产生一管道,然后产生一个子进程。原进程运行顾客程序,而子进程运行服务员程序。

main(){intchpipe1[2],

chpipe2[2];

intpid;

if(pipe(chpipe1)<0)err_sys("pipe1createrror");

if(pipe(chpipe2)<0)err_sys("pipe2createrror"); if((pid=fork())<0){err_sys("can'tfork");} else if(pid){/*

在父进程中

*/close(chpipe1[0]);

close(chpipe2[1]);

pro_client(chpipe2[0],chpipe1[1]); while(wait()!=pid);

/*

等待原子进程运行返回

*/close(chpipe1[1]);

close(chpipe2[0]);

}else{/*

在子进程中

*/close(chpipe1[1]);

close(chpipe2[0]); pro_server(chpipe1[0],chpipe2[1]);

close(chpipe1[0]);

close(chpipe2[1]);

}exit(0);

}

其中顾客子程序为:

#include<stdlio.h> pro_client(rfd,

wfd) intrfd;

intwfd;

{charparstr[]={"parfile.dat"};

intn;

charbuff[100];

n=strlen(parstr);

if(write(wfd,parstr,n)!=n)err_sys("client:

senderror");

if(read(rfd,buff,100)<=0)err_sys("client:

receiveerror");

elseprintf("parentprocess:%s\n",buff);

}

服务员子程序为:

#include<stdlio.h> pro_server(rfd,

wfd) intrfd;

intwfd;

{ intn;

charbuff[1024],

errbuff[64];

intn,

fd;

/*

IPC描述符中读文件名

*/ if((n=read(rfd,buff,1024))<=0) err_sys("server:filereadhaserror");

buff[n]='\0';

if((fd=open(buff,0))<0) { strcpy(errbuf,"filecan'topen.\n");

strcat(buff,errbuff);

n=strlen(buff);

if(write(wfd,buff,n)!=n)err_sys("server:messagesenderror") } else{while((n=read(fd,buff,1024))>0)if(wirte(wfd,buff,n)!=n)err_sys("server:messagesenderror");

if(n<0)err_sys("server:readerror");

}}

程序pro_client中的父进程通过管道发送文件名给子进程,pro_server中的子进程通过管道读文件名,如果接收的文件名有错,文件打不开,则将错误信息回传给client方(父进程);否则,读文件中的信息并将文件信息回传给client。 利用管道的只能在父子进程间的通信,对于两个完全不相关的进程不能采用管道进行通信,这是管道的一个特点。5.2命名管道

命名管道与管道不同之处在于,它与一个路径名关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程无亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信。 系统调用mkfifo()创建命名管道:

intmkfifo(constchar*pathname,

mode_tmode);

该函数的pathname是一个路径名,也就是创建后命名管道的名字,mode打开文件的模式,与打开普通文件的open()函数中文件操作模式参数相似。如果mkfifo的第一个参数是一个已经存在的路径名时,则返回类型为EEXIST的错误;如果返回该错误,那么只要调用打开FIFO的函数就可以了。生成了命名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。

1.通过命名管道发信息

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include<fcntl.h>

#defineFIFO_SERVER"/tmp/fifos_server" main(intargc,char**argv) /*

参数为即将写入的字节数

*/ { intfd,n; charw_buf[512]; intwrite_num; n=100; memset(w_buf,0,512); strncpy(w_buf,argv[1],n); if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("can’tcreatefifos_server\n"); /*

设置非阻塞标志

*/ fd=open(FIFO_SERVER, O_WRONLY|O_NONBLOCK,0); write_num=write(fd,w_buf,n);if(write_wnum==-1) { if(errno==EAGAIN) printf("writetofifoerror;trylater\n"); }else printf("realwritenumis%d\n",

write_num);}

首先,调用函数mkfifo创建命名管道FIFO_SERVER,如果创建成功,则将该管道设置为非阻塞状态。向FIFO_SERVER中写消息,如果成功,则输出这些消息,否则,输出出错信息。

2.通过命名管道收信息

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include<fcntl.h>

#defineFIFO_SERVER"/tmp/fifos_server" main(intargc,char**argv){ charr_buf[4096*2]; intfd; intr_size; intret_size; r_size=atoi(argv[1]); printf("inputrealreadbytes%d\n",r_size); memset(r_buf,0,sizeof(r_buf)); fd=open(FIFO_SERVER, O_RDONLY|O_NONBLOCK,0); /*fd=open(FIFO_SERVER,O_RDONLY,0);在此 处可以把读程序编译成两个

不同版本:阻塞版本及非阻塞版本

*/ if(fd==-1) { printf("open%sforreaderror\n"); exit(); } while(1) { memset(r_buf,0,sizeof(r_buf)); ret_size=read(fd,r_buf,r_size); if(ret_size==-1) if(errno==EAGAIN) printf("nodataavlaible\n");printf("realreadbytes%d\n",ret_size); sleep(1); } pause(); unlink(FIFO_SERVER);}

首先,调用mkfifo创建命名管道FIFO_SERVER,如果创建成功,则以读方式打开该管道[wei2],并将该管道设置为非阻塞状态。通过键盘输入希望读取的字节数,从FIFO_SERVER中读取消息,如果成功,则输出实际读到的字节数,否则,输出出错信息。 下面介绍UNIX系统V提供的三种进程间通信方式。5.3消息通信

利用消息通信,进程可以将具有一定格式的消息发送给任意进程。UNIX系统V为消息通信提供了四个系统调用。

1.生成一个消息队列

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgget(key_tkey,intflags) /*

获取消息队列标识数

*/ /*key:消息队列关键字,长整型

*/*flags:操作标志

*/

其中,参数key是通信双方约定的消息队列关键字,它是一个非负长整型。UNIXIPC通信机构将根据它生成一个消息队列,并返回一个队列标识数ID。队列ID与文件描述字相似,但是进程只要知道该值就可适应它,不必像文件ID那样只有通过继承才能对同一个文件操作。当指定关键字的消息队列存在时,msgget就简单地返回该队列的ID。

参数flags类似于打开和创建文件时的第二个参数o_flags和mode的组合。 2.向消息队列发送一个消息

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgsnd(intqid,structmsgbuf*buf,intnbytes,intflags)

其中,参数qid是消息队列ID,nbytes是消息正文的长度,flags是发送标志。如果flags为零,则当消息队列满时进程阻塞自己;如果flags中IPC以NOWAIT(04000)置位,则消息队列满时msgsnd返回-1,不阻塞进程。

参数buf指定一个由用户定义的消息结构,其基本格式如下:

structmsgtype{ longmtype;

chartext[NBYTES];

}; 其中,mtype是正整数,text[]是长度为NBYTES的正文,其长度是有限制的。

3.从消息队列中接收一个消息

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgrcv(intqid,structmsgbuf*buf,intnbytes,mtype,intflags)

其中,msgrcv中的参数与msgsnd类似,如果flags中的MSG_NOERROR置位,则允许所接收的长度nbytes小于消息正文长度。buf所指的空间大小为不包括mbyte的最大消息正文长度,实际接收的消息长度由msgrcv返回值指出。当mtype=0时,接收消息队列中最早的消息,而不管消息的类型是什么。 4.消息队列的控制

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgctl(intqid,intcmd,structmsqid_ds*sbuf)

其中,msgctl询问队列ID为qid的消息队列的各种特性或者对其进行相应的控制。msqid_ds是消息队列定义的控制结构,包括存取权限结构、队列容量、进程标识和时间等信息。当cmd的取值为IPC_RMID(值为0)时,删除指定的消息队列,释放消息队列标识符;为IPC_SET(值为1)时,将sbuf中的控制信息写到消息队列控制结构中;为IPC_STAT(值为2)时,将消息队列控制结构中的消息写到sbuf中。5.4共享内存

在UNIX中,进程间传递数据的最快方法是让一些相关进程直接共享某些内存区域,而系统V支持任意数据进程对内存的共享。这是一种最快捷高效的方式,系统在内存中指定一个区域作为共享存储区,建立一张段表进行管理,各进程可以申请其中的一个存储段,并在申请时提供关键字。若申请的存储区已经被其他进程所有,则系统向申请进程返回关键字,该存储区就连接到了进程的逻辑地址空间,此后进程就可以直接存取共享存储区中的数据了;若申请的存储段尚未分配,则系统会按照申请者的要求分配存储段,并在段表中加入该进程的信息。一个进程可以申请多个存储段,使用共享存储区进行通信时进程间的互斥或同步要靠其他的机构来解决。

图5-3给出了两个进程P1、P2共享一个关键字为key1的共享存储区的情况。

图5-3进程P1、P2共享一个存储区

共享内存设计的头文件和系统调用是:

#include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> 1.创建一个共享内存段

intshmget(key_tkey,intnbytes,intflags)

三个参数key、nbytes和flags的含义与消息通信中的系统调用msgget类似。key取值IPC_PRIVATE时,新创建的共享内存段的关键字由系统分配。shmget创建共享内存段成功时,初始化相应的控制信息,返回该共享段的描述字ID。 2.将共享内存段映射到进程的虚拟地址空间

char*shmat(intsegid,char*addr,intflags) shmat将标识字为segid的共享内存段映射到由addr参数指定的进程虚拟地址空间。该地址空间可通过brk或者sbrk系统调用动态分配而得到。如果不关心映射内存的地址,则可以置addr为0,让系统选择一个可用地址。Shmat调用成功后返回共享内存段在进程虚拟地址空间的首地址。

3.解除共享内存段的映射

intshmdt(char*addr)/*

共享内存段虚拟地址

*/

参数addr是相应shmdt调用的返回值。Shmdt调用成功时,内存段的访问计数减1,返回值为0。

4.共享内存段控制

intshmct(intsegid,intcmd,structshmid_ds*sbuf)

其中,segid为标识符,cmd为控制字,sbuf为指向共享内存段控制结构指针。

参数cmd取值为SHM_LOCK时,将共享段锁定在内存,禁止换出(超级用户才具有此权限);为SHM_UNLOCK时,与LOCK相反(超级用户才具有此权限);为IPC_RMID、ICP_STAT和IPC_SET时,类似于msgctl中的定义,其中IPC_RMID标志所对应的存储段为“可释放”。

下面介绍关键字的创建函数ftok。

key_t

ftok(char

*pathname,char

proj);

函数根据pathname和proj来创建一个关键字,该键值与pathname相对应。例如:

#defineMSG_FILE"server.c" key_tkey; if((key=ftok(MSG_FILE,'a'))==-1) { fprintf(stderr,"CreatKeyError:%s\a\n",strerror(errno)); exit(1); }

关键字除了可以用ftok创建,也可以使用IPC_PRIVATE表明由系统选择一个关键字。例如:

/*

使用IPC_PRIVATE表示由系统选择一个关键字来创建

*/ /*

创建以后信号灯的初始值为0*/ if((semid=semget(IPC_PRIVATE,1,PERMS))==-1) { fprintf(stderr,"[%d]:AcessSemaphoreError:%s\n\a",

getpid(),strerror(errno)); exit(1); }

父子进程利用共享内存进行通信的程序示例如下:#include

<stdio.h>#include

<string.h>#include

<errno.h>#include

<unistd.h>#include

<sys/stat.h>#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/shm.h>#define

PERM

S_IRUSR|S_IWUSRint

main(int

argc,char

**argv){int

shmid;char

*p_addr,*c_addr;char*name="/dev/shm/myshm1";

if(argc!=2){fprintf(stderr,"Usage:%s\n",argv[0]);exit(1);}key=ftok(name,0);if(key==-1)perror("ftokerror");

shmid=shmget(key,

1024,0777); if(shmid==-1){perror("Create

Share

Memory

Error:");exit(1);}if(fork()){/*

父进程将参数写入到共享内存

*/p_addr=shmat(shmid,0,0);memset(p_addr,'\0',1024);strncpy(p_addr,argv[1],1024);}else{/*

子进程从共享内存读信息

*/c_addr=shmat(shmid,0,0);printf("Client

get

%s",c_addr);exit(0);}}

在使用一个共享内存之前,应调用shmat得到共享内存的起始地址,结束后再用shmdt断开这个内存。在这个程序中,首先创建共享内存,然后产生一个子进程。父进程将参数写入共享内存,之后子进程从共享内存读出写入的内容。5.5信号灯

进程间的互斥和同步可以利用P、V操作实现,但是UNIX系统并没有直接向用户提供这两个操作,而是提供了一组有关信号灯的系统调用。在系统V中的信号灯机制的功能比一般信号灯要强,管理和使用也比较复杂。用户可以一次对一组信号灯进行相同或者不同的操作。

系统V中有关信号灯的头文件和系统调用。

#include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> 1.创建一个信号灯组

intsemget(key_tkey,intnsems,intflags)

其中,key为信号灯组关键字;nsems为信号灯个数;flags为操作标志。 当key为IPC_PRIVATE时,信号灯组关键字由系统选择。flags决定信号灯组的创建方式和权限,其取值和含义与msgget中的flags类似。semget调用成功时,初始化相应的控制块信息,返回信号灯组标识数。

下面是一个打开和创建信号量集的程序段:

intcreat_semaphore_set(key_tkeyval,

intnumsems) { intsid; if(!numsems) return(-1); if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1) { return(-1); } return(sid); };

其中,keyval为键值,用系统调用ftok()返回,参数numsems指出了一个新的信号量集中应该创建的信号量的个数。信号量集中最多的信号量的个数是在linux/sem.h中定义的:

#defineSEMMSL32 /*<=512maxnumofsemaphoresperid*/ 2.对信号灯组的控制

intsemop(intsid,structsembuf**ops,unsignednops)

其中,intsid为信号灯组标识符;ops是对信号灯组进行操作的数据结构;nops为操作个数。semop根据sembuf型的结构数组对标识数为sid的信号灯组中的信号灯进行块操作。在sembuf结构中定义了对编号为sem_num的信号灯要进行的操作。 structsembuf{ shortsem_num;

/*

信号灯编号,从0开始

*/ shortsem_op;

/*

要执行的操作

*/ shortsem_flg;

/*

操作标志

*/ }; 如果sem_op为正数,则信号量将加上它的值。这对应进程释放信号量控制的资源。 如果sem_op为负数,则信号量将减去它的值。IPC_NOWAIT是否被置位决定对信号灯进行的操作,如果没有使用IPC_NOWAIT,那么调用进程将进入睡眠状态,直到信号量控制的资源可以使用为止。

如果sem_op为0,那么对信号灯进行测试。这在一个进程等待完全空闲的资源时使用。

3.信号灯控制

intsemctl(intsid,intsnum,intcmd,unionsemunarg); 其中,sid、snum和cmd分别为信号灯组标识、信号灯编号和控制信令。 联合semun的格式为:

unionsemun{ intval;

structsemid_ds*buf;

/*

指针信号灯集控制块的指针

*/ ushort*array;

};

在semctl调用中,系统根据cmd的主要取值及相关的arg含义为:

GETVAL:将信号灯(sid,snum)的值,存入arg.val。

SETVAL:将信号灯(sid,snum)的值置为arg.val,用于对信号灯初始化。

GETALL:将信号灯组(sid)中所有信号灯的值取到arg.array[]中。

SETALL:将信号灯组(sid)中所有信号灯的值设置为arg.array[]中的值。

IPC_STAT:将信号灯组(sid)的状态信息取到buf结构中。

IPC_SET:将信号灯组(sid)的状态信息设置为buf结构中的信息。

IPC_RMID:删除信号灯组的标识数。

下面的程序段介绍了semctl使用方法:

/*

使用getval命令返回信号量的值,调用中的最后一个参数被忽略

*/ intget_sem_val(intsid,intsemnum)

{ return(semctl(sid,semnum,GETVAL,0)); } /*

下面的程序可以用来初始化一个新的信号量值

*/ voidinit_semaphore(intsid,intsemnum,intinitval) { unionsemunsemopts; semopts.val=initval; semctl(sid,semnum,SETVAL,semopts); } #defineMAX_PRINTERS5 voidprinter_usage(intsid) { inti; for(i=0;i<MAX_PRINTERS;i++) printf("Printer%d:%d",i,get_sem_val(sid,i)); }

其中,sid为信号灯组标识,semnum为信号灯编号。函数get_sem_val返回信号量的值,函数init_semaphore设置信号量的值为initval。函数printer_usage打印出信号灯组sid的信号量的值。5.6UNIX域套接字 UNIX域协议不是一个真正的网络通信协议,它提供同一台计算机上的进程之间进行通信的手段。UNIX域套接字与同一台机器上的域套接字相连,每一个与套接字的新连接都产生一个新的通信管道。同一台机器上的客户机和服务器进程可以通过UNIX域套接字进行通信。与TCP套接字相比,使用UNIX域套接字通信的效率更高。UNIX域套接字经常被用来代替命名管道实现很多重要服务中的IPC,也可以用socketpair()来得到非命名Unix域套接字,与非命名管道类似。 1.命名UNIX域套接字 这种域套接字需要有自己的套接字地址,即UNIX域协议地址结构sockaddr_un:

#include<sys/un.h> structsockaddr_un { shortintsun_family; charsun_path[104]; }

其中,sun_family应设置为AF_UNIX,sun_path成员变量用来指定Linux文件系统的一个文件路径名。也就是说,UNIX域协议使用路径名标识客户机和服务器。UNIX域套接字的定义方式为:

intsocket(AF_UNIX,SOCK_STREAM,0) intsocket(AF_UNIX,SOCK_DGRAM,0)

当服务器或客户机绑定一个UNIX域套接字地址时,按照该路径名创建一个文件。该文件是一个特殊文件,不同于普通的Linux文件,无法用open等系统调用打开或读、写该文件。如果在操作系统提示符下,键入命令“ls–F”查看该文件,可以发现该文件是一个管道类型的文件,其文件名的末尾显示一条“|”线。

函数socket用来创建UNIX域套接字有以下几点要注意:

(1)创建的UNIX域套接字是有名的。创建的域套接字需要绑定未占用的UNIX域套接字相连,服务器需要绑定这个地址,客户机以该地址呼叫连接实现与服务器的连接。UNIX域套接字使用的文件名必须是绝对路径名。

(2)服务器使用UNIX域套接字接收多个客户端的连接请求。同TCP的套接字类似,服务器在绑定地址后,利用listen侦听连接请求,利用accept函数取得一个请求,并返回建立的连接。 (3)客户机调用函数connect呼叫同服务器得到连接。客户机的connect所使用的套接字地址应当是一个已打开的UNIX域套接字,即是服务器绑定过的地址。客户机进程应当拥有打开套接字地址对应文件的权限。若客户机发出连接请求时,侦听队列已满,则connect函数的返回值为错误ECONNREFUSED。

#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/un.h> #include<sys/socket.h>main(intargc,char**argv){intsock_fd,new_fd;structsockaddr_unmy_addr1,my_addr2;if(argc!=2){fprintf(stderr,"usage:UNIXbindpathname\n");exit(1);} if(sock_fd=socket(AF_UNIX,

SOCK_STREAM,

0))==-1) { /*

输出错误提示并退出*/ perror("socket"); exit(1); }/*将要绑定的套接字地址对应的文件删除*/unlike(argv[1]);bzero(&(my_addr1),

sizeof(my_addr1));my_addr1.sun_family=AF_UNIX;strncpy(my_addr1.sun_path,argv[1],sizeof(my_addr1.sun_path)-1);if(bind(sock_fd,

(structsockaddr*)&my_addr1,sizeof(structsockaddr))<0{/*

如果调用bind()失败,则给出错误提示,退出*/perror(“binderror”);exit(1);}getsockname(sock_fd,(structsockaddr*)&my_addr2,&sizeof(my_addr2));printf("bindingtopathname:%s\n",

my_addr2.sun_path,

&sizeof(my_addr2));}

程序中首先创建一个UNIX域套接字,将要绑定的套接字地址对应的文件删除,如果这个文件已存在,函数bind的运行将出错。利用函数strncpy从命令行参数中取得套接字地址,调用bind函数以套接字地址创建一个文件,利用函数getsockname获得绑定地址,并加以显示。 下面给出利用UNIX域套接字进行客户机、服务器间通信的程序示例。

(1)服务器端示例:

#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h>#include<sys/type.h>#include<sys/un.h>#include<sys/socket.h>#defineBACKLOG5#defineSOCK_PATH"/tmp/exmp_sock"main(){intlen,

sin_size,n;intsock_fd,

new_fd; //s2;structsockaddr_unmy_addr,

remote_addr;charbuf[512];if(sock_fd=socket(AF_UNIX,

SOCK_STREAM,

0))==-1){/*

输出错误提示并退出*/perror("socketerror"); exit(1); } my_addr.sun_family=AF_UNIX; strcpy(my_addr.sun_path,SOCK_PATH); /*

将要绑定的套接字地址对应的文件删除

*/ unlike(my_addr.sun_path); len=strlen(my_addr.sun_path)+sizeof(my_addr.sun_family); if(bind(sock_fd,

(structsockaddr*)&my_addr,len)<0) { /*

如果调用bind()失败,则给出错误提示,退出*/

perror("binderror"); exit(1); }if(listen(sockf_d,BACKLOG)==-1){perror("listen");exit(1);}while(1){ sin_size=sizeof(structsockaddr_un);

if((new_fd=accept(sock_fd,(structsockaddr*)&remote_addr,

&sin_size))==-1){ perror("accept");continue;}do{n=recv(new_fd,buf,512,0);if(n<0){perror("recverror");done=1;}if(n==0)done=1;if(!done)if(send(new_fd,buf,n,0)<0){perror("recverror");done=1;}}while(!done);close(new_fd);}//whilereturn0;}(2)客户机示例:#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<string.h>#include<netdb.h>#include<sys/types.h>#include<netinet/in.h>#include<sys/socket.h>#defineSOCK_PATH"exmp_sock"/*

一次所能够接收的最大字节数

*/#defineMAXDATASIZE512intmain(){intsockfd,

n;charbuf[MAXDATASIZE];structsockaddr_untheir_addr;if((sockfd=socket(AF_UNIX,SOCK_STREAM,0))==-1)/ *

如果socket()调用出现错误,则显示错误信息并退出*/

perror("socket");

exit(1); } their_addr.sin_family=AF_INET; /*

网络字节顺序,短整型*/ their_addr.sun_family=AF_UNIX; strcpy(their_addr.sun_path,SOCK_PATH); len=strlen(their_addr.sun_path)+sizeof(their_addr.sun_family); if(connect(so

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论