gogo专业大尺度亚洲高清人体,美女张开双腿让男生桶,亚洲av无码一区二区三区鸳鸯影院,久久久久国产精品人妻

當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 套接字是什么?他的定義如此簡(jiǎn)單

套接字是什么?他的定義如此簡(jiǎn)單 時(shí)間:2018-07-20      來(lái)源:未知

套接字是一種通信機(jī)制,憑借這種機(jī)制,客戶(hù)/服務(wù)器系統(tǒng)的開(kāi)發(fā)工作既可以在本地單機(jī)上進(jìn)行,也可以跨網(wǎng)絡(luò)進(jìn)行,Linux所提供的功能(如打印服務(wù),ftp等)通常都是通過(guò)套接字來(lái)進(jìn)行通信的,套接字的創(chuàng)建和使用與管道是有區(qū)別的,因?yàn)樘捉幼置鞔_地將客戶(hù)和服務(wù)器區(qū)分出來(lái),套接字可以實(shí)現(xiàn)將多個(gè)客戶(hù)連接到一個(gè)服務(wù)器。

套接字屬性

套接字的特性由3個(gè)屬性確定,他們是,域,類(lèi)型和協(xié)議

域指定套接字通信中使用的網(wǎng)絡(luò)介質(zhì),最常見(jiàn)的套接字域是AF_INET,它指的是Internet網(wǎng)絡(luò)

套接字類(lèi)型

一個(gè)套接字可能有多種不同的通信方式

流套接字,流套接字提供一個(gè)有序,可靠,雙向節(jié)流的鏈接,流套接字由類(lèi)型SOCK_STREAM指定,它是在AF_INET域中通過(guò)TCP/IP鏈接實(shí)現(xiàn)的,這就是套接字類(lèi)型(其實(shí)就是通信方式)

與流套接字相反,由類(lèi)型SOCK_DGRAM指定的數(shù)據(jù)報(bào)套接字不建立和維持一個(gè)連接,它對(duì)可以發(fā)送的數(shù)據(jù)長(zhǎng)度有限制,數(shù)據(jù)報(bào)作為一個(gè)單獨(dú)的網(wǎng)絡(luò)消息被傳輸,它可能會(huì)丟失,復(fù)制或亂序

最后一個(gè)是套接字協(xié)議,通常使用默認(rèn)就可以了(也就是最后一個(gè)參數(shù)填0)

創(chuàng)建套接字

socket系統(tǒng)調(diào)用創(chuàng)建一個(gè)套接字并返回一個(gè)描述符,該描述符可以用來(lái)訪(fǎng)問(wèn)該套接字

#include

#include

int socket(int domain,int type,int protocol);

創(chuàng)建的套接字是一條通信線(xiàn)路的一個(gè)端點(diǎn),domain參數(shù)指定協(xié)議族(使用的網(wǎng)絡(luò)介質(zhì)),type參數(shù)指定這個(gè)套接字的通信類(lèi)型(通信方式),protocot參數(shù)指定使用的協(xié)議

domain參數(shù)可以指定如下協(xié)議族

AF_UNIX UNIX域協(xié)議(文件系統(tǒng)套接字)

AF_INET ARPA因特網(wǎng)協(xié)議

AF_ISSO ISO標(biāo)準(zhǔn)協(xié)議

AF_NS Xerox網(wǎng)絡(luò)協(xié)議

AF_IPX Novell IPX協(xié)議

AF_APPLETALK Appletalk DDS協(xié)議

最常用的套接字域是AF_UNIX和AF_INET,前者用于通過(guò)UNIX和Linux文件系統(tǒng)實(shí)現(xiàn)本地套接字

socket函數(shù)的第二個(gè)參數(shù)type指定用于新套接字的特性,它的取值包括SOCK_STREAM和SOCK_DGRAM

SOCK_STREAM是一個(gè)有序,可靠,面向連接的雙向字節(jié)流,一般用這個(gè)

最后一個(gè)protocol參數(shù),將參數(shù)設(shè)為0表示使用默認(rèn)協(xié)議。

套接字地址

每個(gè)套接字(端點(diǎn))都有其自己的地址格式,對(duì)于AF_UNIX套接字來(lái)說(shuō),它的地址由結(jié)構(gòu)sockaddr_un來(lái)描述,該結(jié)構(gòu)體定義在頭文件sys/un.h中,如下:

struct sockaddr_un {

sa_family_t sun_family; //套接字域

char sun_path[];//名字

};

而在AF_INET域中,套接字地址結(jié)構(gòu)由sockaddr_in來(lái)指定,該結(jié)構(gòu)體定義在頭文件netinet/in.h中

struct sockaddr_in {

short int sin_family;//套接字域

unsigned short int sin_port;//接口

struct in_addr sin_addr;

}

IP地址結(jié)構(gòu)in_addr被定義如下:

struct in_addr {

unsigned long int s_addr;

};

命名套接字

要想讓通過(guò)socket調(diào)用創(chuàng)建的套接字可以被其它進(jìn)程使用,服務(wù)器程序就必須給該套接字命名,如下,AF_INET套接字就會(huì)關(guān)聯(lián)到一個(gè)IP端口號(hào)

#include

int bind(int socket,const struct sockaddr *address,size_t address_len);

bind系統(tǒng)調(diào)用把參數(shù)address中的地址分配給與文件描述符socket關(guān)聯(lián)的未命名套接字

創(chuàng)建套接字隊(duì)列

為了能夠在套接字上接受進(jìn)入鏈接,服務(wù)器程序必須創(chuàng)建一個(gè)隊(duì)列來(lái)保存未處理的請(qǐng)求,它用listen系統(tǒng)調(diào)用來(lái)完成這一工作

#include

int listen(int socket,int backlog);

Linux系統(tǒng)可能會(huì)對(duì)隊(duì)列中未處理的連接的最大數(shù)目做出限制

接受連接

一旦服務(wù)器程序創(chuàng)建并命名套接字之后,他就可以通過(guò)accept系統(tǒng)調(diào)用來(lái)等待客戶(hù)建立對(duì)該套接字的連接

#include

int accept(int socket,struct sockaddr *address,size_t *address_len);

accept函數(shù)將創(chuàng)建一個(gè)新套接字來(lái)與該客戶(hù)進(jìn)行通信,并且返回新套接字描述符(這個(gè)描述符和客戶(hù)端中描述符是一樣等同)

請(qǐng)求連接

客戶(hù)程序通過(guò)一個(gè)未命名套接字和服務(wù)器監(jiān)聽(tīng)套接字之間建立的連接的方法來(lái)連接到服務(wù)器,如下:

#include

int connect(int socket,const struct sockaddr *address,size_t address_len);

參數(shù)socket指定的套接字將連接到參數(shù)address指定的服務(wù)器套接字

關(guān)閉套接字

你可以通過(guò)調(diào)用close函數(shù)來(lái)終止服務(wù)器和客戶(hù)上的套接字連接

套接字通信

套接字可以通過(guò)調(diào)用read(),write()系統(tǒng)調(diào)用來(lái)進(jìn)行傳輸數(shù)據(jù)

下面是套接字的一些例子

一個(gè)簡(jiǎn)單的本地客戶(hù)

#include

#include

#include

#include

#include

#include

int main()

{

int sockfd;

int len;

struct sockaddr_un address;//套接字地址

int result;

char ch = 'A';

sockfd = socket(AF_UNIX,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字(端點(diǎn)),并返回一個(gè)描述符

address.sun_family = AF_UNIX;//指明網(wǎng)絡(luò)介質(zhì)

strcpy(address.sun_path,"server_socket");// 名字

len = sizeof(address);

result = connect(sockfd,(struct sockaddr *)&address,len);//請(qǐng)求連接到address

if(result == -1) {

perror("oops: clientl");

exit(1);

}

write(sockfd,&ch,1);//把數(shù)據(jù)寫(xiě)入套接字

read(sockfd,&ch,1);//服務(wù)器處理后讀出處理后數(shù)據(jù)

printf("char form server = %c\n",ch);

close(sockfd);

exit(0);

}

下面是一個(gè)本地服務(wù)器

#include

#include

#include

#include

#include

#include

int main()

{

int server_sockfd,client_sockfd;//定義套接字描述符

int server_len,client_len;

struct sockaddr_un server_address;//套接字地址

struct sockaddr_un client_address;//套接字地址

unlink("server_socket");

server_sockfd = socket(AF_UNIX,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字,并返回一個(gè)描述符

server_address.sun_family = AF_UNIX;//指定網(wǎng)絡(luò)介質(zhì)

strcpy(server_address.sun_path,"server_socket");//名字

server_len = sizeof(server_address);

bind(server_sockfd,(struct sockaddr *)&server_address,server_len);//命名套接字

listen(server_sockfd,5);//創(chuàng)建套接字隊(duì)列

while(1) {

char ch;

printf("server waiting\n");

client_len = sizeof(client_address);

client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);//接受連接

read(client_sockfd,&ch,1);//從套接字中讀取數(shù)據(jù)

ch++;// 處理數(shù)據(jù)

write(client_sockfd,&ch,1);//把數(shù)據(jù)重新寫(xiě)回套接字

close(client_sockfd);

}

}

這兩個(gè)程序運(yùn)行如下:

root@www:/opt/chengxu# ./server1 &

[3] 4644

root@www:/opt/chengxu# server waiting

root@www:/opt/chengxu# ./client1 & ./client1 & ./client1 &

[4] 4652

[5] 4653

[6] 4654

root@www:/opt/chengxu# server waiting

server waiting

server waiting

char form server = B

char form server = B

char form server = B

[4] Done ./client1

[5]- Done ./client1

[6]+ Done

下面看一個(gè)網(wǎng)絡(luò)套接字的例子,先看server程序:

#include

#include

#include

#include

#include

#include

#include

int main()

{

int server_sockfd,client_sockfd;//定義套接字描述符

int server_len,client_len;

struct sockaddr_in server_address;//套接字地址結(jié)構(gòu)體

struct sockaddr_in client_address;//套接字地址結(jié)構(gòu)體

unlink("server_socket");

server_sockfd = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字,并返回一個(gè)描述符

server_address.sin_family = AF_INET;//指定網(wǎng)絡(luò)介質(zhì)

//server_address.sin_addr.s_addr = inet_addr("127.0.0.1");

server_address.sin_addr.s_addr = htonl(INADDR_ANY);//客戶(hù)連接到服務(wù)器的IP

server_address.sin_port = htons(9734);//客戶(hù)連接到端口

server_len = sizeof(server_address);

bind(server_sockfd,(struct sockaddr *)&server_address,server_len);//命名套接字

listen(server_sockfd,5);//創(chuàng)建套接字隊(duì)列

while(1) {

char ch;

printf("server waiting\n");

client_len = sizeof(client_address);

client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);//接受連接

read(client_sockfd,&ch,1);//從套接字中讀取數(shù)據(jù)

ch++;//處理數(shù)據(jù)

write(client_sockfd,&ch,1);//把處理后數(shù)據(jù)寫(xiě)入套接字

close(client_sockfd);//關(guān)閉套接字

}

}

下面是客戶(hù)端程序:

#include

#include

#include

#include

#include

#include

#include

int main()

{

int sockfd;//套接字描述符

int len;

struct sockaddr_in address;//套接字地址結(jié)構(gòu)體

int result;

char ch = 'A';

sockfd = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字并返回一個(gè)描述符

address.sin_family = AF_INET;// 指定網(wǎng)絡(luò)介質(zhì)

address.sin_addr.s_addr = htonl(INADDR_ANY);//要連接到主機(jī)IP

address.sin_port = htons(9734);//要連接到端口號(hào)

len = sizeof(address);

result = connect(sockfd,(struct sockaddr *)&address,len);//請(qǐng)求連接

if(result == -1) {

perror("oops: clientl");

exit(1);

}

write(sockfd,&ch,1);//把數(shù)據(jù)寫(xiě)入套接字

read(sockfd,&ch,1);//服務(wù)器處理完數(shù)據(jù)后從新讀取出來(lái)

printf("char form server = %c\n",ch);

close(sockfd);

exit(0);

}

運(yùn)行這兩個(gè)程序輸出如下:

root@www:/opt/chengxu# ./server2 &

[4] 4746

root@www:/opt/chengxu# server waiting

root@www:/opt/chengxu# ./client2 & ./client2 & ./client2 &

[5] 4749

[6] 4750

[7] 4751

root@www:/opt/chengxu# server waiting

server waiting

server waiting

char form server = B

char form server = B

char form server = B

[5] Done ./client2

[6]- Done ./client2

[7]+ Done ./client2

root@www:/opt/chengxu#

輸出結(jié)果基本和上面的例子差不多,通過(guò)上面程序可以發(fā)現(xiàn)客戶(hù)端寫(xiě)入到套接字中的數(shù)據(jù)可以從服務(wù)器中讀出來(lái),而且客戶(hù)端會(huì)等待服務(wù)器把數(shù)據(jù)讀出處理后再把數(shù)據(jù)讀回來(lái),這有一個(gè)順序,并不會(huì)出現(xiàn)亂序,有點(diǎn)類(lèi)型于管道通信而且是雙向的

主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序

通過(guò)套接字接口傳遞的端口號(hào)和地址都是二進(jìn)制的,不同計(jì)算機(jī)使用不同的字節(jié)序來(lái)表示整數(shù),如32位的整數(shù)分為4個(gè)連續(xù)的字節(jié),并以1-2-3-4存在內(nèi)存中,這里的1表示最高位,也即大端模式,而有的處理器是以4-3-2-1存取的,兩個(gè)不同的計(jì)算機(jī)得到的整數(shù)就會(huì)不一致

為了使不同類(lèi)型的計(jì)算機(jī)可以就通過(guò)網(wǎng)絡(luò)傳輸多字節(jié)的值達(dá)成一致,客戶(hù)和服務(wù)器程序必須在傳出之前,將它們內(nèi)部整數(shù)表示方式轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,它們通過(guò)下面函數(shù)轉(zhuǎn)換(也就是把端口號(hào)等轉(zhuǎn)換成統(tǒng)一網(wǎng)絡(luò)字節(jié)序)

#include

unsigned long int htonl(unsigned long int hostlong);

unsigned short int htons(unsigned short int hostshort);

unsigned long int ntohl(unsigned long int netlong);

unsigned short int ntohs(unsigned short int netshort);

這些函數(shù)將16位和32位整數(shù)在主機(jī)字節(jié)序和標(biāo)準(zhǔn)的網(wǎng)絡(luò)字節(jié)序之間進(jìn)行轉(zhuǎn)換,如上面例子中用到的

address.sin_addr.s_addr = htonl(INADDR_ANY);//要連接到主機(jī)IP

address.sin_port = htons(9734);//要連接到端口號(hào)

這樣就能保證網(wǎng)絡(luò)字節(jié)序的正確,如果你使用的計(jì)算機(jī)上的主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序相同,將看不到任何差異

網(wǎng)絡(luò)信息

到目前為止,我們客戶(hù)和服務(wù)器程序一直是把地址和端口編譯到它們自己的內(nèi)部,對(duì)于一個(gè)更通用的服務(wù)器和客戶(hù)程序來(lái)說(shuō),我們可以通過(guò)網(wǎng)絡(luò)信息函數(shù)來(lái)決定應(yīng)該使用的地址和端口(也就是說(shuō)可以通過(guò)網(wǎng)絡(luò)信息函數(shù)來(lái)獲取IP和端口號(hào)等信息)

類(lèi)似的,如果給定一個(gè)計(jì)算機(jī)名字,你可以通過(guò)調(diào)用解析地址的主機(jī)數(shù)據(jù)庫(kù)函數(shù)來(lái)確定它的IP地址等信息

主機(jī)數(shù)據(jù)庫(kù)函數(shù)在接口文件netdb.h中聲明,如下:

#include

struct hostent *gethostbyaddr(const void *addr,size_t len,int type);

struct hostent *gethostbyname(const char *name);//獲得計(jì)算機(jī)主機(jī)數(shù)據(jù)庫(kù)信息

這些函數(shù)返回的結(jié)構(gòu)體中至少包括以下幾個(gè)成員

struct hostent {

char *h_name; //name of the host

char **h_aliases;//list of aliases

int h_addrtypr;//address type

int h_length;//length in bytes of the address

char **h_addr_list;//list of address

};

如果沒(méi)有與我們查詢(xún)的主機(jī)或地址相關(guān)的數(shù)據(jù)項(xiàng),這些信息函數(shù)將返回一個(gè)空指針

類(lèi)型地,與服務(wù)及相關(guān)聯(lián)端口號(hào)有關(guān)的信息也可以通過(guò)一些服務(wù)信息函數(shù)來(lái)獲取

#include

struct servent *getservbyname(const char *name,const char *proto);//檢查是否有某個(gè)服務(wù)

struct servent *getservbyport(int port,const char *proto);

proto參數(shù)指定用于連接該服務(wù)的協(xié)議,它的兩個(gè)選項(xiàng)tcp和udp,前者用于SOCK_STREAM類(lèi)型

返回結(jié)構(gòu)體中至少包含如下幾個(gè)成員:

struct servent {

char *s_name;//name of the service

char **s_aliases;//list of aliases

int s_port;//The IP port number

char *s_proto;//The service type,usually "tcp" or "udp"

};

如果想獲取某臺(tái)計(jì)算機(jī)主機(jī)數(shù)據(jù)庫(kù)信息,可以調(diào)用gethostbyname函數(shù)并且將結(jié)果打印出來(lái),注意,要把返回的地址表轉(zhuǎn)換為正確的地址類(lèi)型,并用函數(shù)inet_ntoa將它們從網(wǎng)絡(luò)字節(jié)序裝換為可打印的字符串,如下:

#include

char *inet_ntoa(struct in_addr in);

這個(gè)函數(shù)的作用是將一個(gè)因特網(wǎng)主機(jī)地址轉(zhuǎn)換為一個(gè)點(diǎn)分四元租格式的字符串

下面這個(gè)程序getname.c用來(lái)獲取一臺(tái)主機(jī)有關(guān)的信息,如下:

#include

#include

#include

#include

#include

#include

int main(int argc,char *argv[])

{

char *host,**names,**addrs;//接收用到的一些指針

struct hostent *hostinfo;//指向gethostbyname函數(shù)返回的結(jié)構(gòu)體指針

if(argc == 1) {

char myname[256];

gethostname(myname,255);

host = myname;

}

else

host = argv[1];//獲取主機(jī)名

hostinfo = gethostbyname(host);//獲取主機(jī)數(shù)據(jù)庫(kù)

if(!hostinfo) {

fprintf(stderr,"cannot get info for host: %s\n",host);

exit(1);

}

printf("results for host %s:\n",host);

printf("Name: %s\n",hostinfo -> h_name);

printf("Aliases");

names = hostinfo -> h_aliases;

while(*names) {

printf(" %s",*names);

names++;

}

printf("\n");

if(hostinfo -> h_addrtype != AF_INET) {

fprintf(stderr,"not an IP host!\n");

exit(1);

}

//顯示主機(jī)的所有IP地址

addrs = hostinfo -> h_addr_list;

while(*addrs) {

printf(" %s",inet_ntoa(*(struct in_addr *)*addrs));

addrs++;

}

printf("\n");

exit(0);

}

運(yùn)行這個(gè)程序輸出如下所示:

root@www:/opt/chengxu# ./getname

results for host www:

Name: www.kugoo.com

Aliases www

127.0.1.1

root@www:/opt/chengxu# ./getname localhost

results for host localhost:

Name: localhost

Aliases ip6-localhost ip6-loopback

127.0.0.1 127.0.0.1

root@www:/opt/chengxu#

下面一個(gè)例子是連接到標(biāo)準(zhǔn)服務(wù)

#include

#include

#include

#include

#include

#include

int main(int argc,char *argv[])//argc表示參數(shù)個(gè)數(shù),argv[]表示具體參數(shù)程序名為argv[0]

{

char *host;

int sockfd;

int len,result;

struct sockaddr_in address;//套接字地址結(jié)構(gòu)體

struct hostent *hostinfo;//指向gethostbyname函數(shù)返回的結(jié)構(gòu)體指針

struct servent *servinfo;//指向getservbyname函數(shù)返回的結(jié)構(gòu)體指針

char buffer[128];

if(argc == 1)

host = "localhost";

else

host = argv[1];

hostinfo = gethostbyname(host);//獲取主機(jī)數(shù)據(jù)庫(kù)信息

if(!hostinfo) {

fprintf(stderr,"no host: %s\n",host);

exit(1);

}

//檢查主機(jī)上是否有daytime服務(wù)

servinfo = getservbyname("daytime","tcp");

if(!servinfo) {

fprintf(stderr,"no daytime service\n");

exit(1);

}

sockfd = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字,并返回一個(gè)描述符

address.sin_family = AF_INET;//使用網(wǎng)絡(luò)介質(zhì)

address.sin_port = servinfo -> s_port;//端口號(hào)

address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list;//獲取主機(jī)IP地址

len = sizeof(address);

result = connect(sockfd,(struct sockaddr *)&address,len);//請(qǐng)求連接

if(result == -1) {

perror("oops:getdate");

exit(1);

}

result = read(sockfd,buffer,sizeof(buffer));

buffer[result] = '\0';

printf("read %d bytes: %s",result,buffer);

close(sockfd);

exit(0);

}

運(yùn)行這個(gè)程序輸出如下:

root@www:/opt/chengxu# ./getname1 localhost

oops:getdate: Connection refused

root@www:/opt/chengxu#

之所以這樣是因?yàn)槲业奶摂M機(jī)中沒(méi)有啟動(dòng)daytime這個(gè)服務(wù)

我用的是ubuntu虛擬機(jī),下面來(lái)啟動(dòng)這個(gè)daytime服務(wù)看一下

首先

root@www:/opt/chengxu# vim /etc/inetd.conf

進(jìn)入到inetd的配置文件把這個(gè)服務(wù)前面的#號(hào)去掉,改成如下:

18 #discard dgram udp wait root internal

19 daytime stream tcp nowait root internal

20 #time stream tcp nowait root internal

然后保存退出

接下來(lái)重啟一下服務(wù)就可以了通過(guò)下面命令啟動(dòng)(或重啟)xinetd服務(wù)(xinetd和openbsd-inetd差不多都是屬于守護(hù)進(jìn)程)

root@www:/opt/chengxu# /etc/init.d/openbsd-inetd restart

* Restarting internet superserver inetd [ OK ]

root@www:/opt/chengxu#

可以通過(guò)下面命令看一下daytime這個(gè)服務(wù)是否處于監(jiān)聽(tīng)狀態(tài)了,如下:

root@www:/opt/chengxu# netstat -a | grep daytime

tcp 0 0 *:daytime *:* LISTEN

root@www:/opt/chengxu#

下面再重新運(yùn)行一下上面程序看看:

root@www:/opt/chengxu# ./getname1 localhost

read 26 bytes: Sun Sep 23 23:15:14 2012

root@www:/opt/chengxu#

因特網(wǎng)守護(hù)進(jìn)程

當(dāng)有客戶(hù)端連接到某個(gè)服務(wù)時(shí),守護(hù)進(jìn)程就運(yùn)行相應(yīng)的服務(wù)器,這使得針對(duì)各項(xiàng)網(wǎng)絡(luò)服務(wù)器不需要移植運(yùn)行著,我們通常是通過(guò)一個(gè)圖形界面來(lái)配置xinetd,ubuntu用的好像是openbsd-inetd這 個(gè)守護(hù)進(jìn)程,我們可以直接修改它的配置文件,ubuntu對(duì)應(yīng)的是/etc/inetd.conf這個(gè)文件,就拿我們上面那個(gè)daytime這個(gè)服務(wù)為 例,在ubuntu中它默認(rèn)是關(guān)閉的,這時(shí)我們要進(jìn)入它的配置文件里,把它前面的#字符去掉就可以了,修改了的配置文件如下:

17 #discard stream tcp nowait root internal

18 #discard dgram udp wait root internal

19 daytime stream tcp nowait root internal

20 #time stream tcp nowait root internal

21

22 #:STANDARD: These are standard services.

23

24 #:BSD: Shell, login, exec and talk are BSD protocols.

25

26 #:MAIL: Mail, news and uucp services.

27

28 #:INFO: Info services

29

30 #:BOOT: TFTP service is provided primarily for booting. Most sites

31 # run this only on machines acting as "boot servers."

32 bootps dgram udp wait root /usr/sbin/bootpd bootpd -i -t 120

33 tftp dgram udp wait nobody /usr/sbin/tcpd /usr/sbin/in.tftpd /srv/tftproot

34 #bootps dgram udp wait root /usr/sbin/bootpd bootpd -i -t 120

之后重啟一下守護(hù)進(jìn)程就可以了,如下:

root@www:/opt/chengxu# /etc/init.d/openbsd-inetd restart

* Restarting internet superserver inetd [ OK ]

root@www:/opt/chengxu# netstat -a | grep daytime

tcp 0 0 *:daytime *:* LISTEN

root@www:/opt/chengxu#

多客戶(hù)

到目前為止,一直都是介紹如何用套接字來(lái)實(shí)現(xiàn)本地的和跨網(wǎng)絡(luò)的客戶(hù)/服務(wù)器系統(tǒng),一旦連接建立,套接字連接的行為就類(lèi)似于打開(kāi)底層文件描述符,而且在很多方面類(lèi)似雙向管道,現(xiàn)在我們來(lái)考慮多個(gè)用戶(hù)同時(shí)連接一個(gè)服務(wù)器的情況,下面是一個(gè)例子:

#include

#include

#include

#include

#include

#include

#include

int main()

{

int server_sockfd,client_sockfd;//定義套接字描述符

int server_len,client_len;

struct sockaddr_in server_address;//套接字地址結(jié)構(gòu)體

struct sockaddr_in client_address;//套接字地址結(jié)構(gòu)體

server_sockfd = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字,并返回一個(gè)描述符

server_address.sin_family = AF_INET;//網(wǎng)絡(luò)介質(zhì)

server_address.sin_addr.s_addr = htonl(INADDR_ANY);//要連接到的服務(wù)器IP地址

server_address.sin_port = htons(9734);//要連接到的端口號(hào)

server_len = sizeof(server_address);

bind(server_sockfd,(struct sockaddr *)&server_address,server_len);//命名套接字

listen(server_sockfd,5);//創(chuàng)建套接字隊(duì)列

signal(SIGCHLD,SIG_IGN);

while(1) {

char ch;

printf("server waiting\n");

client_len = sizeof(client_address);

client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);//接收連接

if(fork() == 0) {//創(chuàng)建一個(gè)子進(jìn)程用于傳輸數(shù)據(jù)

read(client_sockfd,&ch,1);

sleep(5);

ch++;

write(client_sockfd,&ch,1);

close(client_sockfd);

exit(0);

}

else {

close(client_sockfd);//在父進(jìn)程中關(guān)閉打開(kāi)的套接字描述符

}

}

}

下面是這個(gè)程序的運(yùn)行情況

root@www:/opt/chengxu# ./server4 &

root@www:/opt/chengxu# ./client2 & ./client2 & ./client2 &

[6] 6742

[7] 6743

[8] 6744

server waiting

root@www:/opt/chengxu# server waiting

server waiting

char form server = B

char form server = B

char form server = B

[6] Done ./client2

[7]- Done ./client2

[8]+ Done ./client2

root@www:/opt/chengxu#

上面服務(wù)器程序用到了子進(jìn)程來(lái)處理要處理數(shù)據(jù),這樣一來(lái)就可以多個(gè)客戶(hù)連接到服務(wù)器了

select系統(tǒng)調(diào)用

select系統(tǒng)調(diào)用允許程序同時(shí)在多個(gè)底層文件描述符上等待用戶(hù)輸入(或完成輸出),檢測(cè)讀,寫(xiě),異常文件描述符集,select函數(shù)對(duì)數(shù)據(jù)結(jié)構(gòu)fd_set進(jìn)行操作(也就是說(shuō)對(duì)文件描述符集進(jìn)行操作),有一組定義好的宏可以用來(lái)控制這些文件描述符集,如下:

#include

#include

void FD_ZERO(fd_set *fdset);//初始化(清零)文件描述符集

void FD_CLR(int fd,fd_set *fdset);//清除文件描述符集

void FD_SET(int fd,fd_set *fdset);//把文件描述符fd加到文件描述符集中

int FD_ISSET(int fd,fd_set *fdset);//檢測(cè)文件描述符變化情況

select函數(shù)還可以用一個(gè)超時(shí)值來(lái)防止無(wú)限期阻塞,這個(gè)超時(shí)值由一個(gè)timeval結(jié)構(gòu)給出,如下:

struct timeval {

time_t tv_sec;

long tv_usec;

}

select系統(tǒng)調(diào)用的原型如下所示:

#include

#include

int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

select系統(tǒng)調(diào)用用于測(cè)試文件描述符集合中,是否有一個(gè)文件描述符已經(jīng)處于可讀狀態(tài)或可寫(xiě)狀態(tài)或錯(cuò)誤狀態(tài),它將阻塞等待某個(gè)文件描述符進(jìn)入上述狀態(tài)。

select函數(shù)會(huì)在發(fā)生如下情況時(shí)返回:readfds集合中描述符可讀(也就是寫(xiě)入數(shù)據(jù)了,寫(xiě)完數(shù)據(jù)后會(huì)發(fā)送一個(gè)可讀信號(hào)),writefds集合中 有描述符可寫(xiě)或errorfds集合中有描述符錯(cuò)誤,如果這三種情況都沒(méi)有發(fā)生,select函數(shù)將在timrout指定的超時(shí)后返回,這時(shí)所有文件描述 符集合將被清空

下面是一個(gè)select系統(tǒng)調(diào)用的例子:

#include

#include

#include

#include

#include

#include

#include

int main()

{

char buffer[128];

int result,nread;

fd_set inputs,testfds;//定義文件描述符集合

struct timeval timeout;//定義超時(shí)結(jié)構(gòu)體

FD_ZERO(&inputs);//清零文件描述符集合

FD_SET(0,&inputs);//把標(biāo)準(zhǔn)輸入加到文件描述符集合中

while(1) {

testfds = inputs;

timeout.tv_sec = 2;

timeout.tv_usec = 500000;

result = select(FD_SETSIZE,&testfds,(fd_set *)NULL,(fd_set *)NULL,&timeout);//檢測(cè)是否有標(biāo)準(zhǔn)輸入,沒(méi)有的話(huà)每隔2.5秒打印一次timeout

switch(result) {

case 0:

printf("timeout\n");

break;

case -1:

perror("select");

exit(1);

default:

if(FD_ISSET(0,&testfds)) {//檢測(cè)是否有標(biāo)準(zhǔn)輸入

ioctl(0,FIONREAD,&nread);//得到緩沖區(qū)里有多少字節(jié)要被讀取,存到nread中

if(nread == 0) {

printf("keyboard done\n");

exit(0);

}

nread = read(0,buffer,nread);//從標(biāo)準(zhǔn)輸入緩沖區(qū)中讀取數(shù)據(jù)

buffer[nread] = 0;

printf("read %d from keyboard: %s",nread,buffer);

}

break;

}

}

}

下面是這個(gè)程序運(yùn)行時(shí)情況

root@www:/opt/chengxu# ./select

timeout

hello

read 6 from keyboard: hello

timeout

timeout

fread

read 6 from keyboard: fread

keyboard done

root@www:/opt/chengxu#

最后來(lái)看一個(gè)改進(jìn)的多客戶(hù)/服務(wù)器程序:

#include

#include

#include

#include

#include

#include

#include

#include

int main()

{

int server_sockfd,client_sockfd;//定義套接字描述符

int server_len,client_len;

struct sockaddr_in server_address;//套接字地址結(jié)構(gòu)體

struct sockaddr_in client_address;//套接字地址結(jié)構(gòu)體

int result;

fd_set readfds,testfds;//定義文件描述符集合

server_sockfd = socket(AF_INET,SOCK_STREAM,0);//創(chuàng)建一個(gè)套接字,并返回一個(gè)描述符

server_address.sin_family = AF_INET;//網(wǎng)絡(luò)介質(zhì)

server_address.sin_addr.s_addr = htonl(INADDR_ANY);//要連接到服務(wù)器的Ip地址

server_address.sin_port = htons(9734);//要連接到的端口號(hào)

server_len = sizeof(server_address);

bind(server_sockfd,(struct sockaddr *)&server_address,server_len);//命名套接字

listen(server_sockfd,5);// 創(chuàng)建套接字隊(duì)列

FD_ZERO(&readfds);//清零描述符

FD_SET(server_sockfd,&readfds);//把server_sockfd描述符加到readfds描述符集合中

while(1) {

char ch;

int fd;

int nread;

testfds = readfds;

printf("server waiting\n");

result = select(FD_SETSIZE,&testfds,(fd_set *)0,(fd_set *)0,(struct timeval *)0);//檢測(cè)testfds描述符集合是否有變化,沒(méi)有的話(huà)阻塞等待

if(result < 1) {

perror("server5");

exit(1);

}

for(fd = 0;fd < FD_SETSIZE;fd++) {//逐個(gè)檢查描述符

if(FD_ISSET(fd,&testfds)) {//檢測(cè)那個(gè)描述符集合發(fā)生變化,這個(gè)很重要

if(fd == server_sockfd) {//如果是server_sockfd描述符集變化

client_len = sizeof(client_address);

client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);//接收連接

FD_SET(client_sockfd,&readfds);//把客戶(hù)端的client_sockfd描述符加進(jìn)描述符集合中,這樣就可以用select來(lái)檢測(cè)客戶(hù)端的描述符了

printf("adding client on fd %d\n",client_sockfd);

}

else {

ioctl(fd,FIONREAD,&nread);//能得到緩沖區(qū)里有多少字節(jié)要被讀取,存進(jìn)nread中

if(nread == 0) {

close(fd);

FD_CLR(fd,&readfds);

printf("removing client on fd %d\n",fd);

}

else {

read(fd,&ch,1);

sleep(50);

printf("serving client on fd %d\n",fd);

ch++;

write(fd,&ch,1);

}

}

}

}

}

}

這個(gè)程序運(yùn)行如下:

root@www:/opt/chengxu# ./server5 &

[6] 7344

root@www:/opt/chengxu# server waiting

root@www:/opt/chengxu# ./client2 & ./client2 & ./client2 &

[7] 7352

[8] 7353

[9] 7354

root@www:/opt/chengxu# server waiting

server waiting

server waiting

char form server = B

char form server = B

char form server = B

[7] Done ./client2

[8]- Done ./client2

[9]+ Done ./client2

root@www:/opt/chengxu#

....

上一篇:nm命令使用詳解,讓你加快學(xué)習(xí)速度

下一篇:stm32串口編程實(shí)例-實(shí)現(xiàn)數(shù)據(jù)的收發(fā)功能

熱點(diǎn)文章推薦
華清學(xué)員就業(yè)榜單
高薪學(xué)員經(jīng)驗(yàn)分享
熱點(diǎn)新聞推薦
前臺(tái)專(zhuān)線(xiàn):010-82525158 企業(yè)培訓(xùn)洽談專(zhuān)線(xiàn):010-82525379 院校合作洽談專(zhuān)線(xiàn):010-82525379 Copyright © 2004-2022 北京華清遠(yuǎn)見(jiàn)科技集團(tuán)有限公司 版權(quán)所有 ,京ICP備16055225號(hào)-5,京公海網(wǎng)安備11010802025203號(hào)

回到頂部