创建套接口 在这一部分,我们将会看到创建套接口与创建管道一样的容易。虽然有一些我们将会了解到的函数参数。为了能创建成功,这些参数必须提供合适的值。 socketpair函数概要如下: #include #include int socketpair(int domain, int type, int protocol, int sv[2]); sys/types.h文件需要用来定义一些C宏常量。sys/socket.h文件必须包含进来定义socketpair函数原型。 socketpair函数需要四个参数。他们是: 套接口的域 套接口类型 使用的协议 指向存储文件描述符的指针 domain参数直到第2单我们才会解释。对于socketpair函数而言,只需提供C宏AF_LOCAL。 类型参数声明了我们希望创建哪种类型的套接口。socketpair函数的选择如下: SOCK_STREAM SOCK_DGRAM 套接口类型的选择我们将会在第4章谈到。在这一章中,我们只需要简单的使用SOCK_STREAM套接口类型。 对于socketpair函数,protocol参数必须提供为0。 参数sv[2]是接收代表两个套接口的整数数组。每一个文件描述符代表一个套接口,并且与另一个并没有区别。 如果函数成功,将会返回0值。否则将会返回-1表明创建失败,并且errno来表明特定的错误号。 使用socketpair的例子 为了演示如何使用socketpair函数,我们用下面的例子来进行演示。 1: /* Listing 1.1: 2: * 3: * Example of socketpair(2) function: 4: */ 5: #include 6: #include 7: #include 8: #include 9: #include 10: #include 11: #include 12: 13: int 14: main(int argc,char **argv) { 15: int z; /* Status return code */ 16: int s[2]; /* Pair of sockets */ 17: 18: /* 19: * Create a pair of local sockets: 20: */ 21: z = socketpair(AF_LOCAL,SOCK_STREAM,0,s); 22: 23: if ( z == -1 ) { 24: fprintf(stderr, 25: "%s: socketpair(AF_LOCAL,SOCK_STREAM,0)\n", 26: strerror(errno)); 27: return 1; /* Failed */ 28: } 29: 30: /* 31: * Report the socket file descriptors returned: 32: */ 33: printf("s[0] = %d;\n",s[0]); 34: printf("s[1] = %d;\n",s[1]); 35: 36: system("netstat --unix -p"); 37: 38: return 0; 39: } 演示程序的描述如下: 1 在第16行声明数组s[2]用来存储用来引用两个新创建的套接口的文件描述符。 2 在第21行调用socketpair函数。domain参数指定为AF_LOCAL,套接口类型参数指定为SOCK_STREAM,而协议指定为0。 3 23行的if语句用来测试socketpair函数是否成功。如果z的值为-1,就会向标准错误发送报告,并且在27行退出程序。 4 如果函数调用成功,控制语句就会转到33,并且在34行向标准输出报告返回的文件单元数。 5 36行使用system函数来调用netstat命令。命令选项--unix表明只报告Unix套接口,-p选项则是要报告进程信息。 使用提供的Makefile,我们可以用make命令来编译这个程序: $ make 01lst01 gcc -c -D_GNU_SOURCE -Wall 01LST01.c gcc 01LST01.o -o 01lst01 为了执行这个演示程序,我们可以执行下面的命令: $ ./01lst01 程序的执行结果如下: 1: $ ./01lst01 2: s[0] = 3; 3: s[1] = 4; 4: (Not all processes could be identified, non-owned process info 5: will not be shown, you would have to be root to see it all.) 6: Active UNIX domain sockets (w/o servers) 7: Proto RefCnt Flags Type . . . I-Node PID/Program name Path 8: unix 1 [] STREAM . . . 406 - @00000019 9: unix 1 [] STREAM . . . 490 - @0000001f 10: unix 1 [] STREAM . . . 518 - @00000020 11: unix 0 [] STREAM . . . 117 - @00000011 12: unix 1 [] STREAM . . . 789 - @00000030 13: unix 1 [] STREAM . . . 549 - @00000023 14: unix 1 [] STREAM . . .1032 662/01lst01 15: unix 1 [] STREAM . . .1031 662/01lst01 16: unix 1 [] STREAM . . . 793 - /dev/log 17: unix 1 [] STREAM . . . 582 - /dev/log 18: unix 1 [] STREAM . . . 574 - /dev/log 19: unix 1 [] STREAM . . . 572 - /dev/log 20: unix 1 [] STREAM . . . 408 - /dev/log 21: $ 在我们上面的输入显示中,在第1行调用可执行程序01LST01。第2行和第3行显示了我们在文件描述符3和4上打开套接口。接下来的4到20行是程序中netstat命令的输出。 尽管这个程序并没有使用创建的套接口来做任何事情,但是他确实演示了套接口的创建。并且他演示了套接口单元数的分配与打开的文件的方式一样。 在套接口上执行I/O操作 我们在前面已经了解到套接口可以像任何打开的文件一样向其中写入或是从中读取。在这一部分将我们将会亲自演示这一功能。然而为了试都讨论的完整,我们先来看一下read,write,close的函数概要: #include ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd); 这些应是我们已经熟悉的Linux的输入/输入函数。通过回顾我们可以看到,read函数返从文件描述符fd中返回最大count字节的输入,存放到buf缓冲区中。返回值代表实际读取的字节数。如果返回0则代表文件结束。 write函数将我们指定的buf缓冲区中总计count的字节写入文件描述符fd中。返回值代表实际写入的字节数。通常这必须与指定的count参数相匹配。然而也会有一些情况,这个值要count小,但是我们没有必要担心这样的情况。 最后,如果文件成功关闭close就会返回0。对于这些函数,如果返回-1则表明有错误发生,并且错误原因将会发送到外部变量errno中。为了可以访问这个变量,我们需要在源文件中包含errno.h头文件。 下面的例子是在套接口的两个方向上执行读取与写入操作。 /***************************************** * * Listing 1.2 * * Example performing I/O on s socket pair: * * ******************************************/ #include #include #include #include #include #include #include
int main(int argc,char **argv) { int z; /* Status return code */ int s[2]; /* Pair of sockets */ char *cp; /* A work pointer */ char buf[80]; /* work buffer */
/* * As of RedHat 6.0,these are still not defined: */ #ifndef SHUT_WR #define SHUT_RD 0 #define SHUT_WR 1 #define SHUT_RDWR 2 #endif
/* * Main program */ int main(int argc,char **argv) { int z; /* Status return code */ int s[2]; /* Pair of sockets */ char *msgp; /* A message pointer */ int mlen; /* Message length */ char buf[80]; /* work buffer */ pid_t chpid; /* Child PID */
/* * Create a pair of local sockets: */ z = socketpair(AF_LOCAL,SOCK_STREAM,0,s); if(z == -1) { fprintf(stderr,"%s:socketpair(2)\n",strerror(errno)); exit(1); }
/* * Now fork() into two processes: */ if((chpid = fork()) == (pid_t)-1) { /* * Failed to fork into two processes: */ fprintf(stderr,"%s:fork(2)\n",strerror(errno)); exit(1); } else if(chpid == 0) { /* * This is child process(client) */ char rxbuf[80]; /*Receive buffer*/ printf ("Parent PID is %ld\n",(long)getppid()); close(s[0]); /* Server uses s[1] */ s[0] = -1; /*Forget this unit */
/* * Form the message and its length: */ msgp = "%A %d-%b-%Y %l:%M %p"; mlen = strlen(msgp);
z = write(s[1],msgp,mlen); if(z<0) { fprintf(stderr,"%s:write(2)\n",strerror(errno)); exit(1); }
/* * Now indicate that we will not be writing * anything further to our socket,by shutting * down the write side of the socket: */ if(shutdown(s[1],SHUT_WR) == -1) { fprintf(stderr,"%s:shutdown(2)\n",strerror(errno)); exit(1); }
/* * Recevie the reply from the server: */ z = read(s[1],rxbuf,sizeof rxbuf); if(z<0) { fprintf(stderr,"%s:read(2)\n",strerror(errno)); exit(1); }
/* * Put a null byte at the end of what we * received from the server: */ rxbuf[z]=0;
/* * Report the result: */ printf("Server returned '%s'\n",rxbuf); fflush(stdout); close(s[1]); /*Close our end now*/ } else { /* * This is parent process(server): */
int status; /*Child termintation status*/ char txbuf[80]; /*Reply buffer*/ time_t td; /*Current date&time*/ printf("Child PID is %ld\n",(long)chpid); fflush(stdout);
/* * Wait for a request from the client: */ z = read(s[0],buf,sizeof buf); if(z<0) { fprintf(stderr,"%s:read(2)\n",strerror(errno)); exit(1); } /* * Put a null byte at the end of the * message we recevied from the client: */ buf[z] = 0;
/* * Now perform the server function on * the received message */ time(&td); /* Get current time */ strftime(txbuf,sizeof txbuf, /* Buffer */ buf, /* Input fromate*/ localtime(&td));/* Input time */ /* * Send back the response to client: */ z = write (s[0],txbuf,strlen(txbuf)); if(z<0) { fprintf(stderr,"%s:write(2)\n",strerror(errno)); exit(1); } /* * Close our end of the socket */ close(s[0]);
/* * Wait for the child process to exit: */ waitpid(chpid,&status,0);