在這一部分,我們將會(huì)看到創(chuàng)建套接口與創(chuàng)建管道一樣的容易。雖然有一些我們將會(huì)了解到的函數(shù)參數(shù)。為了能創(chuàng)建成功,這些參數(shù)必須提供合適的值。
socketpair函數(shù)概要如下:
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
sys/types.h文件需要用來定義一些C宏常量。sys/socket.h文件必須包含進(jìn)來定義socketpair函數(shù)原型。
socketpair函數(shù)需要四個(gè)參數(shù)。他們是:
套接口的域
套接口類型
使用的協(xié)議
指向存儲(chǔ)文件描述符的指針
domain參數(shù)直到第2單我們才會(huì)解釋。對(duì)于socketpair函數(shù)而言,只需提供C宏AF_LOCAL。
類型參數(shù)聲明了我們希望創(chuàng)建哪種類型的套接口。socketpair函數(shù)的選擇如下:
SOCK_STREAM
SOCK_DGRAM
套接口類型的選擇我們將會(huì)在第4章談到。在這一章中,我們只需要簡(jiǎn)單的使用SOCK_STREAM套接口類型。
對(duì)于socketpair函數(shù),protocol參數(shù)必須提供為0。
參數(shù)sv[2]是接收代表兩個(gè)套接口的整數(shù)數(shù)組。每一個(gè)文件描述符代表一個(gè)套接口,并且與另一個(gè)并沒有區(qū)別。
如果函數(shù)成功,將會(huì)返回0值。否則將會(huì)返回-1表明創(chuàng)建失敗,并且errno來表明特定的錯(cuò)誤號(hào)。
使用socketpair的例子
為了演示如何使用socketpair函數(shù),我們用下面的例子來進(jìn)行演示。
1:? /* Listing 1.1:
2:?? *
3:?? * Example of socketpair(2) function:
4:?? */
5:? #include <stdio.h>
6:? #include <stdlib.h>
7:? #include <unistd.h>
8:? #include <errno.h>
9:? #include <string.h>
10: #include <sys/types.h>
11: #include <sys/socket.h>
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行聲明數(shù)組s[2]用來存儲(chǔ)用來引用兩個(gè)新創(chuàng)建的套接口的文件描述符。
2 在第21行調(diào)用socketpair函數(shù)。domain參數(shù)指定為AF_LOCAL,套接口類型參數(shù)指定為SOCK_STREAM,而協(xié)議指定為0。
3 23行的if語句用來測(cè)試socketpair函數(shù)是否成功。如果z的值為-1,就會(huì)向標(biāo)準(zhǔn)錯(cuò)誤發(fā)送報(bào)告,并且在27行退出程序。
4 如果函數(shù)調(diào)用成功,控制語句就會(huì)轉(zhuǎn)到33,并且在34行向標(biāo)準(zhǔn)輸出報(bào)告返回的文件單元數(shù)。
5 36行使用system函數(shù)來調(diào)用netstat命令。命令選項(xiàng)--unix表明只報(bào)告Unix套接口,-p選項(xiàng)則是要報(bào)告進(jìn)程信息。
使用提供的Makefile,我們可以用make命令來編譯這個(gè)程序:
$ make 01lst01
gcc -c -D_GNU_SOURCE -Wall 01LST01.c
gcc 01LST01.o -o 01lst01
為了執(zhí)行這個(gè)演示程序,我們可以執(zhí)行下面的命令:
$ ./01lst01
程序的執(zhí)行結(jié)果如下:
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行調(diào)用可執(zhí)行程序01LST01。第2行和第3行顯示了我們?cè)谖募枋龇?和4上打開套接口。接下來的4到20行是程序中netstat命令的輸出。
盡管這個(gè)程序并沒有使用創(chuàng)建的套接口來做任何事情,但是他確實(shí)演示了套接口的創(chuàng)建。并且他演示了套接口單元數(shù)的分配與打開的文件的方式一樣。
在套接口上執(zhí)行I/O操作
我們?cè)谇懊嬉呀?jīng)了解到套接口可以像任何打開的文件一樣向其中寫入或是從中讀取。在這一部分將我們將會(huì)親自演示這一功能。然而為了試都討論的完整,我們先來看一下read,write,close的函數(shù)概要:
#include <unistd.h>
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);
這些應(yīng)是我們已經(jīng)熟悉的Linux的輸入/輸入函數(shù)。通過回顧我們可以看到,read函數(shù)返從文件描述符fd中返回最大count字節(jié)的輸入,存放到buf緩沖區(qū)中。返回值代表實(shí)際讀取的字節(jié)數(shù)。如果返回0則代表文件結(jié)束。
write函數(shù)將我們指定的buf緩沖區(qū)中總計(jì)count的字節(jié)寫入文件描述符fd中。返回值代表實(shí)際寫入的字節(jié)數(shù)。通常這必須與指定的count參數(shù)相匹配。然而也會(huì)有一些情況,這個(gè)值要count小,但是我們沒有必要擔(dān)心這樣的情況。
最后,如果文件成功關(guān)閉close就會(huì)返回0。對(duì)于這些函數(shù),如果返回-1則表明有錯(cuò)誤發(fā)生,并且錯(cuò)誤原因?qū)?huì)發(fā)送到外部變量errno中。為了可以訪問這個(gè)變量,我們需要在源文件中包含errno.h頭文件。
下面的例子是在套接口的兩個(gè)方向上執(zhí)行讀取與寫入操作。
/*****************************************
?*
?* Listing 1.2
?*
?* Example performing I/O on s socket pair:
?*
?* ******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
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 */
?? ?/*
?? ? * Create a pair of local sockets:
?? ? */
?? ?z = socketpair(AF_LOCAL,SOCK_STREAM,0,s);
?? ?if(z == -1)
?? ?{
?? ??? ?fprintf(stderr,
?? ??? ??? ??? ?"%s:socketpair(AF_LOCAL,SOCK_STREAM,""0)\n",strerror(errno));
?? ??? ?return 1;?? ?/* Failed */
?? ?}
?? ?/*
?? ? * Write a message to socket s[1]:
?? ? */
?? ?z = write(s[1],cp="Hello?",6);
?? ?if(z<0)
?? ?{
?? ??? ?fprintf(stderr,"%s:wirte(%d,\"%s\",%d)\n",strerror(errno),s[1],cp,strlen(cp));
?? ??? ?return 2;?? ?/* Failed */
?? ?}
?? ?printf("Wrote message '%s' on s[1]\n",cp);
?? ?/*
?? ? * Read from socket s[0]:
?? ? */
?? ?z = read(s[0],buf,sizeof buf);
?? ?if(z < 0)
?? ?{
?? ??? ?fprintf(stderr,"%s:read(%d,buf,%d)\n",
?? ??? ??? ??? ?strerror(errno),s[0],sizeof buf);
?? ??? ?return 3;?? ?/* Failed */
?? ?}
?? ?/*
?? ? * Report received message:
?? ? */
?? ?buf[z] = 0;?? ?/* NUL terminate */
?? ?printf("Recevie message '%s' from socket s[0]\n",buf);
?? ?/*
?? ? * Send a reply back to s[1] from s[0]:
?? ? */
?? ?z = write(s[0],cp="Go away!",8);
?? ?if(z < 0)
?? ?{
?? ??? ?fprintf(stderr,"%s:write(%d,\"%s\",%d)\n",
?? ??? ??? ??? ?strerror(errno),s[0],cp,strlen(cp));
?? ??? ?return 4;?? ?/* Failed */
?? ?}
?? ?printf("Wrote message '%s' on s[0]\n",cp);
?? ?/*
?? ? * Read from socket s[1]:
?? ? */
?? ?z = read(s[1],buf,sizeof buf);
?? ?if(z < 0)
?? ?{
?? ??? ?fprintf(stderr,"%s:read(%d,buf,%d)\n",
?? ??? ??? ??? ?strerror(errno),s[1],sizeof buf);
?? ??? ?return 3;?? ?/* Failed */
?? ?}
?? ?/*
?? ? * Report message recevied by s[0]:
?? ? */
?? ?buf[z] = 0;?? ?/*NUL terminate */
?? ?printf("Received message '%s' from socket s[1]\n",
?? ??? ??? ?buf);
?? ?/*
?? ? * Close the sockets:
?? ? */
?? ?close(s[0]);
?? ?close(s[1]);
?? ?puts("Done");
?? ?return 0;
}
程序調(diào)用的步驟總結(jié)如下:
1 在第23行調(diào)用socketpair函數(shù),如果成功返回,則將生成的套接口存放在數(shù)組s中。
2 在第25行測(cè)試函數(shù)是否成功,如果發(fā)生錯(cuò)誤,將會(huì)報(bào)告錯(cuò)誤。
3 在第36行一個(gè)由6個(gè)字符組成的消息"Hello?"寫入套接口s[1]。注意并沒有寫入空字節(jié),因?yàn)樵趙rite函數(shù)的count參數(shù)中僅指定了6個(gè)字節(jié)。
4 第37到第42行檢測(cè)并報(bào)告可能發(fā)生的錯(cuò)誤。
5 第44行聲明一個(gè)成功寫操作。
6 在第49行read調(diào)用試著從另一個(gè)套接口s[0]讀取消息。在這條語句中,可以讀取任何最大為buf[]數(shù)組尺寸的消息。
7 第50行到第55行檢測(cè)并服務(wù)在read語句中可能發(fā)生的錯(cuò)誤。
8 第60行到第62行報(bào)告一條成功接收的消息。
9 第67行到第73行向套接口s[0]寫入一條回復(fù)消息"Go away!"。這就演示了不同于管道,信息在可以作為端點(diǎn)的套接口中雙向傳送。
10 第75行聲明一個(gè)成功的寫操作。
11 第80行到第86行應(yīng)從通信線路的另一個(gè)端點(diǎn)套接口s[1]中讀取信息"Go away!"。
12 第91行到第93行報(bào)告成功接收的信息。
13 這兩個(gè)套接口在第98行和第99行關(guān)閉。
14 在第102行程序退出。
當(dāng)程序被調(diào)用時(shí),輸出結(jié)果如下:
$ ./01lst02
Wrote message 'Hello?' on s[1]
Received message 'Hello?' from socket s[0]
Wrote message 'Go away!' on s[0]
Received message 'Go away!' from socket s[1]
Done.
$
如果我們跟蹤我們?cè)谇懊嫠串嫷牟襟E,我們就會(huì)發(fā)現(xiàn)信息是在套接口中雙向傳送的。而且我們演示了套接口用與文件相同的方式來關(guān)閉。
關(guān)閉套接口
在前面,我們看到如何來創(chuàng)建一對(duì)套接口,并且看到如何使用這些套接口來執(zhí)行最基本的輸入與輸出操作。我們也可以看到套接口可以使用與通過調(diào)用close函數(shù)來關(guān)閉文件的方式來關(guān)閉。現(xiàn)在我們來了解一下關(guān)閉套接口所提供的函數(shù)。
當(dāng)從通過pipe函數(shù)創(chuàng)建的管道中讀取時(shí),當(dāng)接收到一個(gè)文件結(jié)尾時(shí),接收就會(huì)認(rèn)為不會(huì)再有要接收的數(shù)據(jù)。當(dāng)關(guān)閉管道的寫端時(shí),文件結(jié)束的條件是通過寫進(jìn)程發(fā)送的。
同樣的過程也可以用在套接口上。當(dāng)另一個(gè)端點(diǎn)關(guān)閉時(shí),接收端就會(huì)收到一個(gè)文件結(jié)束的標(biāo)識(shí)。
當(dāng)本地進(jìn)程希望通知遠(yuǎn)程端不再接收數(shù)據(jù)時(shí)就會(huì)出現(xiàn)問題。如果本地進(jìn)程關(guān)閉了他的套接口,這是可以適用的。然而,如果他希望從遠(yuǎn)程端接收一個(gè)確認(rèn)信息,這是不可能的,因?yàn)楝F(xiàn)在他的套接口已經(jīng)關(guān)閉了。這樣的情況需要一個(gè)半關(guān)閉套接口的方法。
shutdown函數(shù)
下面顯示了shutdown函數(shù)的概要:
#include <sys/socket.h>
int shutdown(int s, int how);
shutdown函數(shù)需要兩個(gè)參數(shù)。他們是:
套接口描述符s指定了要部分關(guān)閉的套接口。
參數(shù)how指定要如何關(guān)閉這個(gè)套接口中。
如果函數(shù)成功則返回0。如果調(diào)用失敗則會(huì)返回-1,錯(cuò)誤原因?qū)?huì)發(fā)送到errno。
how的可能值如下:
值?? ?宏?? ??? ?描述
0?? ?SHUT_RD?? ??? ?在指定的套接口上不再允許讀操作。
1?? ?SHUT_WR?? ??? ?在指定的套接口上不再允許寫操作。
2?? ?SHUT_RDWR?? ?在指定的套接口上不再允許讀寫操作。
注意當(dāng)how值指定為2時(shí),這個(gè)函數(shù)的調(diào)用與close函數(shù)調(diào)用相同。
關(guān)閉向一個(gè)套接口的寫
下面的代碼演示了如何指定在本地的套接口上不再執(zhí)行寫操作:
int z;
int s;? /* Socket */
z = shutdown(s, SHUT_WR);
if ( z == -1 )
??? perror("shutdown()");
關(guān)閉套接口的寫端解決了一系列難題。他們是:
清空包含任何要發(fā)送的數(shù)據(jù)的內(nèi)核緩沖區(qū)。通過內(nèi)核網(wǎng)絡(luò)軟件來緩沖數(shù)據(jù)來改進(jìn)性能。
向遠(yuǎn)程套接口發(fā)送文件結(jié)束標(biāo)識(shí)。這就通知遠(yuǎn)程讀進(jìn)程在這個(gè)套接口上不會(huì)再向他發(fā)送數(shù)據(jù)。
保留半關(guān)閉套接口為讀打開。這就使得在套接口上發(fā)送了文件結(jié)束標(biāo)識(shí)以后還可以接收確認(rèn)信息。
丟棄在這個(gè)套接口上的打開引用計(jì)數(shù)。只有最后在這個(gè)套接口上的close函數(shù)將會(huì)發(fā)送一個(gè)文件結(jié)束標(biāo)識(shí)。
處理復(fù)制的套接口
如果一個(gè)套接口文件描述符通過dup或者是dup2函數(shù)來調(diào)用進(jìn)行復(fù)制,只有最后的close函數(shù)調(diào)用可以關(guān)閉這個(gè)套接口。這是因?yàn)榱硗鈴?fù)制的文件描述符仍處于使用狀態(tài)。如下面的代碼如演示的樣子:
int s;????? /* Existing socket */
int d;????? /* Duplicated socket */
d = dup(s); /* duplicate this socket */
close(s);?? /* nothing happens yet */
close(d);?? /* last close, so shutdown socket */
在這個(gè)例子中,第一個(gè)close函數(shù)調(diào)用不會(huì)有任何效果。先關(guān)閉其中的任何一個(gè)都是一樣的結(jié)果。關(guān)閉s或者d將會(huì)為同一個(gè)套接口保留一個(gè)文件描述符。只有通過close函數(shù)調(diào)用來關(guān)閉最后一個(gè)存在的文件描述符才會(huì)有效果。在這個(gè)例子中,關(guān)閉d文件描述符關(guān)閉了這個(gè)套接口。
shutdown函數(shù)避免了這種區(qū)別。重復(fù)這個(gè)例子代碼,通過使用shutdown函數(shù)解決了這個(gè)問題:
int s;??????? /* Existing socket */
int d;??????? /* Duplicated socket */
d = dup(s);?? /* duplicate this socket */
shutdown(s,SHUT_RDWR); /* immediate shutdown */
盡管套接口s也在文件單元d上打開,shutdown函數(shù)立刻使得套接口執(zhí)行關(guān)閉操作。這個(gè)操作在打開的文件描述符s和d都是同樣的效果,因?yàn)樗麄冎赶蛲粋€(gè)套接口。
這個(gè)問題出現(xiàn)的另一個(gè)方式就是執(zhí)行了fork函數(shù)調(diào)用。任何優(yōu)先級(jí)高于fork操作的套接口都會(huì)在子進(jìn)程中被復(fù)制。
關(guān)閉從一個(gè)套接口讀
關(guān)閉套接口的讀取端將會(huì)使得待讀取的任何數(shù)據(jù)都會(huì)被忽略掉。如果從遠(yuǎn)程套接口發(fā)送來更多的數(shù)據(jù),也同樣會(huì)被忽略掉。然而任何試著從這個(gè)套接口進(jìn)行讀取的進(jìn)程都會(huì)返回一個(gè)錯(cuò)誤。這通常用來強(qiáng)制協(xié)議或是調(diào)試代碼。
shutdown函數(shù)的錯(cuò)誤代碼如下:
錯(cuò)誤?? ??? ?描述
EBADF?? ??? ?指定的套接口不是一個(gè)可用的文件描述符
ENOTSOCK?? ?指定的文件描述符不是一個(gè)套接口
ENOTCONN?? ?指定的套接口并沒有連接
從這個(gè)表中我們可以看到,對(duì)于已連接的套接口應(yīng)只調(diào)用shutdown函數(shù),否則就會(huì)返回ENOTCONN錯(cuò)誤代碼。
編寫一個(gè)客戶/服務(wù)器例子
現(xiàn)在我們所了解的套接口API的集合已經(jīng)足夠讓我們開始一些有趣的嘗試了。在這一部分,我們會(huì)檢測(cè),編譯并且測(cè)試一個(gè)簡(jiǎn)單的通過套接口進(jìn)行通信的客戶與服務(wù)器進(jìn)程。
為了使得這個(gè)程序盡可能的小,將會(huì)啟動(dòng)一個(gè)程序,然后復(fù)制為一個(gè)客戶進(jìn)程與一個(gè)服務(wù)器進(jìn)程。子進(jìn)程將會(huì)是客戶端程序角色,而原始的父進(jìn)程將會(huì)執(zhí)行服務(wù)器的角色。下圖顯示了父進(jìn)程與子進(jìn)程的關(guān)系以及套接口的使用。

父進(jìn)程是最初啟動(dòng)的程序。他立刻通過調(diào)用socketpair函數(shù)來生成一對(duì)套接口,然后通過調(diào)用fork函數(shù)將自己復(fù)制為兩個(gè)進(jìn)程。
服務(wù)器將會(huì)接收請(qǐng)求,執(zhí)行請(qǐng)求,然后退出。類似的客戶端將會(huì)執(zhí)行請(qǐng)求,報(bào)告服務(wù)器響應(yīng),然后退出。
請(qǐng) 求將會(huì)采用strftime函數(shù)的第三個(gè)參數(shù)的格式。這是一個(gè)用來格式化日期與時(shí)間字符串的格式字符串。服務(wù)器將會(huì)在接收到請(qǐng)求時(shí)得到當(dāng)前的日期與時(shí)間。 服務(wù)器將會(huì)使用客戶端的請(qǐng)求字符串來將其格式化為最終的字符串,然后發(fā)送給客戶端。我們先來回顧一個(gè)strftime函數(shù)的概要:
#include <time.h>
size_t strftime(char *buf,
??? size_t max,
??? const char *format,
??? const struct tm *tm);
參數(shù)buf與max分別指定了輸出緩沖區(qū)以及最大長(zhǎng)度。參數(shù)format是一個(gè)輸入字符串,可以允許我們來格式化日期與時(shí)間字符串。最后參數(shù)tm用來指定必須來創(chuàng)建輸出日期與時(shí)間字符串的日期與時(shí)間組件。
/*****************************************
?*
?* Listing 1.3
?*
?* Client/Server Example Using socketpair
?* and fork:
?*
?* ******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <time.h>
/*
?* 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);
?? ??? ?printf("Child sending request '%s'\n",msgp);
?? ??? ?fflush(stdout);
?? ??? ?/*
?? ??? ? * Write a request to the server:
?? ??? ? */
?? ??? ?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);
?? ??? ?close(s[1]);?? ?/* Cient uses s[0] */
?? ??? ?s[1] = -1;?? ?/* Forget this desciptor */
?? ??? ?/*
?? ??? ? * 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);
?? ?}
?? ?return 0;
}
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
