這里的“通訊”加上了引號(hào),是因?yàn)閷?shí)際上所有的socket都有通訊的功能,只是在我們的例子中,之前那個(gè)socket只負(fù)責(zé)listen,而這個(gè)socket負(fù)責(zé)接受信息并echo回去。
我們現(xiàn)看看這個(gè)函數(shù):
bool TcpServer::isAccept()
{
unsigned int clntAddrLen = sizeof(clntAddr);
if ( (communicationSock = accept(listenSock, (sockaddr*)&clntAddr, &clntAddrLen)) < 0 ) {
return false;
} else {
std::cout << "Client(IP: " << inet_ntoa(clntAddr.sin_addr) << ") connected.\n";
return true;
}
}
{
unsigned int clntAddrLen = sizeof(clntAddr);
if ( (communicationSock = accept(listenSock, (sockaddr*)&clntAddr, &clntAddrLen)) < 0 ) {
return false;
} else {
std::cout << "Client(IP: " << inet_ntoa(clntAddr.sin_addr) << ") connected.\n";
return true;
}
}
用accept()創(chuàng)建新的socket
在我們的例子中,communicationSock實(shí)際上是用函數(shù)accept()創(chuàng)建的。
int accept(int socket, struct sockaddr* clientAddress, unsigned int* addressLength);
在Linux中的實(shí)現(xiàn)為:/* Await a connection on socket FD.
When a connection arrives, open a new socket to communicate with it,
set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
peer and *ADDR_LEN to the address's actual length, and return the
new socket's descriptor, or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int accept (int __fd, __SOCKADDR_ARG __addr,
socklen_t *__restrict __addr_len);
這個(gè)函數(shù)實(shí)際上起著構(gòu)造socket作用的僅僅只有第一個(gè)參數(shù)(另外還有一個(gè)不在這個(gè)函數(shù)內(nèi)表現(xiàn)出來(lái)的因素,后面會(huì)討論到),后面兩個(gè)指針都有副作用,在socket創(chuàng)建后,會(huì)將客戶(hù)端sockaddr的數(shù)據(jù)以及結(jié)構(gòu)體的大小傳回。When a connection arrives, open a new socket to communicate with it,
set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
peer and *ADDR_LEN to the address's actual length, and return the
new socket's descriptor, or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int accept (int __fd, __SOCKADDR_ARG __addr,
socklen_t *__restrict __addr_len);
當(dāng)程序調(diào)用accept()的時(shí)候,程序有可能就停下來(lái)等accept()的結(jié)果。這就是我們前一小節(jié)說(shuō)到的block(阻塞)。這如同我們調(diào)用std::cin的時(shí)候系統(tǒng)會(huì)等待輸入直到回車(chē)一樣。accept()是一個(gè)有可能引起block的函數(shù)。請(qǐng)注意我說(shuō)的是“有可能”,這是因?yàn)閍ccept()的block與否實(shí)際上決定與第一個(gè)參數(shù)socket的屬性。這個(gè)文件描述符如果是block的,accept()就block,否則就不block。默認(rèn)情況下,socket的屬性是“可讀可寫(xiě)”,并且,是阻塞的。所以,我們不修改socket屬性的時(shí)候,accept()是阻塞的。
accept()的另一面connect()
accept()只是在server端被動(dòng)的等待,它所響應(yīng)的,是client端connect()函數(shù):
int connect(int socket, struct sockaddr* foreignAddress, unsigned int addressLength);
雖然我們這里不打算詳細(xì)說(shuō)明這個(gè)client端的函數(shù),但是我們可以看出來(lái),這個(gè)函數(shù)與之前我們介紹的bind()有幾分相似,特別在Linux的實(shí)現(xiàn)中:/* Open a connection on socket FD to peer at ADDR (which LEN bytes long).
For connectionless socket types, just set the default address to send to
and the only address from which to accept transmissions.
Return 0 on success, -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);
connect() 也使用了const的sockaddr,只不過(guò)是遠(yuǎn)程電腦上的而非bind()的本機(jī)。For connectionless socket types, just set the default address to send to
and the only address from which to accept transmissions.
Return 0 on success, -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);
accept()在server端表面上是通過(guò)listen socket創(chuàng)建了新的socket,實(shí)際上,這種行為是在接受對(duì)方客戶(hù)機(jī)程序中connect()函數(shù)的請(qǐng)求后發(fā)生的。綜合起看,被創(chuàng)建的新socket實(shí)際上包含了listen socket的信息以及客戶(hù)端connect()請(qǐng)求中所包含的信息——客戶(hù)端的sockaddr地址。
新socket與sockaddr的關(guān)系
accept()創(chuàng)建的新socket(我們例子中的communicationSock,這里我們簡(jiǎn)單用newSock來(lái)帶指)首先包含了listen socket的信息,所以,newSock具有本機(jī)sockaddr的信息;其次,因?yàn)樗憫?yīng)于client端connect()函數(shù)的請(qǐng)求,所以,它還包含了clinet端sockaddr的信息。
我們說(shuō)過(guò),stream流形式的TCP協(xié)議實(shí)際上是建立起一個(gè)“可來(lái)可去”的通道。用于listen的通道,遠(yuǎn)程機(jī)的目標(biāo)地址是不確定的;但是newSock卻是有指定的本機(jī)地址和遠(yuǎn)程機(jī)地址,所以,這個(gè)socket,才是我們真正用于TCP“通訊”的socket。
inet_ntoa()
#include <arpa/inet.h>
/* Convert Internet number in IN to ASCII representation. The return value
is a pointer to an internal array containing the string. */
extern char *inet_ntoa (struct in_addr __in) __THROW;
對(duì)于這個(gè)函數(shù),我們可以作為一種,將IP地址,由in_addr結(jié)構(gòu)轉(zhuǎn)換為可讀的ASCII形式的固定用法。/* Convert Internet number in IN to ASCII representation. The return value
is a pointer to an internal array containing the string. */
extern char *inet_ntoa (struct in_addr __in) __THROW;
posted on 2008-07-15 13:04 lf426 閱讀(4208) 評(píng)論(0) 編輯 收藏 引用 所屬分類(lèi): SDL入門(mén)教程 、Linux與C++ 、socket 編程入門(mén)教程