要實(shí)現(xiàn)服務(wù)器和客戶端之間完整的收發(fā)消息,需要處理以下的幾個(gè)問題。
粘包半包問題
粘包就是一次從socket緩沖區(qū)中讀取到的數(shù)據(jù)不止一條消息。半包就是一次從socket緩沖區(qū)讀取的數(shù)據(jù)只是一條消息的部分。
要解決粘包半包的問題,常用的方法有長度信息法、固定長度法和結(jié)束符號(hào)法。一般的游戲開發(fā)中使用的長度信息法。
長度信息法在要發(fā)送的信息前面,加上一個(gè)固定長度的信息長度信息,解析的時(shí)候,先讀出這個(gè)固定長度的信息數(shù)據(jù),然后讀取信息長度的數(shù)據(jù)??梢姡@個(gè)方法的核心是實(shí)現(xiàn)一個(gè)能從socket緩沖區(qū)讀取指定長度數(shù)據(jù)的函數(shù)。
從socket緩沖區(qū)中讀取指定長度的數(shù)據(jù),可以參考《unix網(wǎng)絡(luò)編程》中的readn函數(shù)。
ssize_t /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* and call read() again */
else
return(-1);
} else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
/* end readn */
不完整發(fā)送問題
當(dāng)socket的緩沖區(qū)滿了的時(shí)候,再往緩沖區(qū)寫數(shù)據(jù),寫入的數(shù)據(jù)可能小于要發(fā)送的數(shù)據(jù)。如果不對這種情況進(jìn)行處理,那發(fā)送的數(shù)據(jù)就可能不完整。因此,我們需要一個(gè)向緩沖區(qū)寫入固定長度數(shù)據(jù)的函數(shù),同樣,我們也可以參考《UNIX網(wǎng)絡(luò)編程》中的寫法。
ssize_t /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
/* end writen */
大端小端問題
因?yàn)橄⑶懊嬗幸粋€(gè)表示長度的數(shù)字,而數(shù)字的表示有大端小端之分,要處理這個(gè)問題,只需要規(guī)定傳輸?shù)臄?shù)字是大端表示還是小端表示。然后,在傳輸?shù)臅r(shí)候進(jìn)行判斷,如果和傳輸?shù)谋硎痉绞讲煌M(jìn)行轉(zhuǎn)換即可。在收到數(shù)字的時(shí)候,使用自定義的函數(shù)來解析這個(gè)數(shù)字。
連接斷開的問題
游戲開發(fā)中,客戶端通常要和服務(wù)器保持TCP連接,這時(shí)候,就需要處理連接斷開問題了。當(dāng)發(fā)送消息的時(shí)候,需要檢查連接是否正常。關(guān)閉的時(shí)候,要檢查緩沖區(qū)的數(shù)據(jù)是否已經(jīng)發(fā)送完畢,要等數(shù)據(jù)發(fā)送完畢才能關(guān)閉。
我對這方面不是很熟,先寫這么多吧。