tcp是基於位元組流的,你向send或者recv提交乙個buffer,它不一定幫你傳送或者接收完這個buffer的,所以你應該自己改寫一下這兩個函式,以便符合應用的需要,改寫後姑且命名為tcpsend,tcprecv
下面我改寫後的這兩個函式的實現:
int tcpsend(socket s,const char*buf,int len,int flags)
length-=n;
sendcount+=n; }
return sendcount; // 返回已傳送的位元組數}
int tcprecv(socket s,char *buf,int len,int flags)
length-=nrev;
recvcount+=nrev;}
return recvcount; //返回接收到的位元組數}
int recv_function()
;while(1)
else
data_lenght = *(int*)buffer);
if( data_lenght<=offset )
else}}
return 0;
}int ret = send(socket, buffer, len );
二、使用tcp協議編寫檔案傳輸程式的三個問題
1、無法區分訊息邊界
平時在談論tcp時,我們都能夠注意到它與udp的一點區別,那就是tcp的傳輸是流式的,而udp的傳輸是面向資料報的,但是一般的同學在第一次 編寫傳輸程式時,卻沒有意識到這一點。實際上,tcp的流式傳輸,體現在程式設計上,就是tcp不為應用程式保留訊息邊界。也就是說在一端呼叫send(), 傳送一定的位元組數,譬如2k個位元組,在另一端呼叫recv()時,也許只收到其中的前1k個位元組,需要再呼叫一次recv()才能把所有的位元組收完整。這 是因為在接收端的tcp程式收到合適數量的位元組數之後或者快取滿之後,就會把緩衝區中的資料提交給應用程式,這時recv()函式返回,但實際上還有一些 位元組在網路上傳送。若傳送端呼叫兩次send()分別傳送了0.5k和2k個位元組,接收端第一次接收可能會接收到1k個位元組,第二次會接受到剩下的 1.5k個位元組。如果傳送的0.5k和2k個位元組分別代表伺服器傳送給客戶的兩個檔案,接收方就無法區分兩次收到的資料分別是哪個檔案中的內容了。
這種特點在客戶與伺服器需要進行一定數量的互動應答時顯得非常不方便,傳送的訊息全部雜糅在一起,區分不出邊界,需要程式設計師自己處理邊界的問題。
2、不恰當的使用傳送函式
另外乙個問題是一些同學使用迴圈,按乙個位元組乙個位元組的傳送檔案的內容,這樣做是非常有害的。雖然程式把資料推送到緩衝區後,tcp會盡量把資料集 合成mss長度傳送出去,但是若資料到達的速率比較慢,即使不到乙個mss的長度,tcp等待超過一段時間後,也會導致一次傳送。另外,在關閉了 nagle演算法後,這種一次要求tcp傳送乙個位元組的函式呼叫的效率會更低,如果檔案比較大,速度會慢得不能忍受。消費ip和tcp的較長頭部,卻只帶走 很少數量位元組的淨荷,對頻寬的利用率就變低了。
三、檔案傳輸程式的
設計思路
1、區分訊息邊界
在鏈路層的協議中,區分訊息邊界用的是位填充與字元填充法。在本程式中,
使用另外一種辦法。不使用填充法是因為無論是字元填充還是位填充,都要掃瞄 一遍所有的資料,尋找出與邊界相衝突的模式。這在鏈路層是比較
合理的,因為要計算楨檢驗資訊,必然要做全部的掃瞄,這時就可以順便找出衝突並進行填充。但 是在應用層的程式中,如果每次都要把所有的資料都掃瞄一遍,會減慢處理資料的速度。
作者的辦法是先發4位元組定長的資料表示將來要傳送的訊息的長度,再一次把整個訊息都推送給send()函式,放到緩衝區中,讓tcp協議
自己決定何 時從緩衝區中取出資料、取多少資料傳送到網路上。接收方每次先接收4位元組的資料,確定將來接收資料的長度,根據這個資訊,再不停的呼叫recv()函式, 並累積已經收到的位元組數,直到達到傳送方一開始通告的數量為止。這期間收到的資料視為乙個訊息,下次再接收資料時還是先收4個位元組。
這個方法不需要做額外的計算,就解決了區分訊息邊界的問題,在傳送大量資料(g以上)時,效率要比把資料全掃瞄一遍並填充的效率高。
2、恰當的使用傳送函式
在呼叫傳送函式時,不是乙個位元組乙個位元組的傳送,而是一次傳送一塊資料。資料塊的大小只要適當即可,推送到緩衝區後,tcp協議會自行決定最佳的傳送策略。
3、區分控制資訊和資料
在這之前,我們已經解決了區分訊息邊界的問題。乙個訊息實際上是一塊資料,我們可以在資料的頭部劃分出一定的空間存放控制資訊,在其餘的部分存放數 據。在本程式中,作者提出了「命令」的概念。乙個命令包含命令的名字和命令的資料。命令名既用來區分不同的命令,又用來表示各命令特定的語義;命令的資料 根據命令的不同,其含義不同。在本程式中,有如下三個命令:
命令req_file,表示客戶向伺服器請求乙個檔案,命令名為「req_file」,資料部分是要請求檔案的null結尾的字串路徑;
命令resp_data,表示伺服器向客戶傳送的檔案的一塊資料,命令名為「resp_data」,資料部分是檔案的資料塊;
命令resp_fin,這個命令在伺服器傳送完乙個檔案的所有資料後傳送,表示傳送檔案結束,命令名為「resp_fin」,資料部分為空。
recv函式和send函式
int recv socket s,char far buf,int len,int flags 不論是客戶還是伺服器應用程式都用recv函式從tcp連線的另一端接收資料。該函式的第乙個引數指定接收端套接字描述符 第二個引數指明乙個緩衝區,該緩衝區用來存放recv函式接收到的資料 第三個引數指明bu...
recv函式和send函式
recv函式 int recv socket s,char far buf,int len,int flags 不論是客戶還是伺服器應用程式都用recv函式從tcp連線的另一端接收資料。該函式的第乙個引數指定接收端套接字描述符 第二個引數指明乙個緩衝區,該緩衝區用來存放recv函式接收到的資料 第三...
send函式和recv函式
1.send 函式 int send socket s,const char far buf,int len,int flags 不論是客戶還是伺服器應用程式都用send函式來向tcp連線的另一端傳送資料。客戶程式一般用send函式向伺服器傳送請求,而伺服器則通常用send函式來向客戶程式傳送應答。...