tcp 傳輸是基於流的方式。send() 只是把數據放到發送緩衝區,而recv() 只是從內核緩衝區取出現在的數據。不能send 一次 recv 一次。
例如你send 的數據大,那麼造成分成多個包發送過來那麼 recv() 一次是錯誤的。
那麼我們在循環裏 recv() 什麼時候停止呢。
正常情況:
1.你自己的包裏帶長度,服務器端首先接收到長度,然後不停的按長度接收,接收滿那些長度,則此次發送完畢。
2.包尾結束符,不停的收,收到結束符則視作此次發送完畢。
異常情況:
1.recv() 返回0 說明發送端斷開鏈接。那麼接收端可以終止。
2.recv() 返回 -1,errno 會被置爲一下的值。
EAGAIN:套接字已標記爲非阻塞,而接收操作被阻塞或者接收超時
EBADF:sock不是有效的描述詞
ECONNREFUSE:遠程主機阻絕網絡連接
EFAULT:內存空間訪問出錯
EINTR:操作被信號中斷
EINVAL:參數無效
ENOMEM:內存不足
ENOTCONN:與面向連接關聯的套接字尚未被連接上
ENOTSOCK:sock索引的不是套接字 當返回值是0時,爲正常關閉連接;
返回值<0時並且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情況下認爲連接是正常的,繼續接收。
解決辦法:
我們先封裝底層的可以接受的系統錯誤。
recvSafe( int sock, void* buffer, int len, flage = 0){
do
{
if (sock == -1) throw InvalidSocketException();
rc = ::recv(sock,
reinterpret_cast<char*>(buffer), leng, flags);
}while (rc < 0 && errno == EINTR
|| errno == EWOULDBLOCK || errno == EAGAIN);
return rc;
}
上面的代碼幫我們屏蔽了一些底層的應該繼續讀的錯誤,我們在這個的基礎上while(1) 循環裏接收,接收到之後按達到協議規定的長度或者接收到結束字符串就停止接收。
void task()
{
char buf[1024];
int rLen = 0;
while(1)
{
rlen = recvSafe( buf, rLen );
char *ret = strstr( buf, "\r\n");
if( ret )
{
break;
}
}
...
}