這里的“通訊”加上了引號,是因為實際上所有的socket都有通訊的功能,只是在我們的例子中,之前那個socket只負責listen,而這個socket負責接受信息并echo回去。
我們現(xiàn)看看這個函數(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;
- }
- }
用accept()創(chuàng)建新的socket
在我們的例子中,communicationSock實際上是用函數(shù)accept()創(chuàng)建的。
- int accept(int socket, struct sockaddr* clientAddress, unsigned int* addressLength);
在Linux中的實現(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);
這個函數(shù)實際上起著構造socket作用的僅僅只有第一個參數(shù)(另外還有一個不在這個函數(shù)內(nèi)表現(xiàn)出來的因素,后面會討論到),后面兩個指針都有副作用,在socket創(chuàng)建后,會將客戶端sockaddr的數(shù)據(jù)以及結構體的大小傳回。
當程序調(diào)用accept()的時候,程序有可能就停下來等accept()的結果。這就是我們前一小節(jié)說到的block(阻塞)。這如同我們調(diào)用std::cin的時候系統(tǒng)會等待輸入直到回車一樣。accept()是一個有可能引起block的函數(shù)。請注意我說的是“有可能”,這是因為accept()的block與否實際上決定與第一個參數(shù)socket的屬性。這個文件描述符如果是block的,accept()就block,否則就不block。默認情況下,socket的屬性是“可讀可寫”,并且,是阻塞的。所以,我們不修改socket屬性的時候,accept()是阻塞的。
accept()的另一面connect()
accept()只是在server端被動的等待,它所響應的,是client端connect()函數(shù):
- int connect(int socket, struct sockaddr* foreignAddress, unsigned int addressLength);
雖然我們這里不打算詳細說明這個client端的函數(shù),但是我們可以看出來,這個函數(shù)與之前我們介紹的bind()有幾分相似,特別在Linux的實現(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,只不過是遠程電腦上的而非bind()的本機。
accept()在server端表面上是通過listen socket創(chuàng)建了新的socket,實際上,這種行為是在接受對方客戶機程序中connect()函數(shù)的請求后發(fā)生的。綜合起看,被創(chuàng)建的新socket實際上包含了listen socket的信息以及客戶端connect()請求中所包含的信息——客戶端的sockaddr地址。
新socket與sockaddr的關系
accept()創(chuàng)建的新socket(我們例子中的communicationSock,這里我們簡單用newSock來帶指)首先包含了listen socket的信息,所以,newSock具有本機sockaddr的信息;其次,因為它響應于client端connect()函數(shù)的請求,所以,它還包含了clinet端sockaddr的信息。
我們說過,stream流形式的TCP協(xié)議實際上是建立起一個“可來可去”的通道。用于listen的通道,遠程機的目標地址是不確定的;但是newSock卻是有指定的本機地址和遠程機地址,所以,這個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;
對于這個函數(shù),我們可以作為一種,將IP地址,由in_addr結構轉(zhuǎn)換為可讀的ASCII形式的固定用法。



