/************************************* 文件名: server.c TCP 組包和拆包實現算法 作者: 馬中海 QQ: 284358503 Email: zhonghaima001@163.com */ #include <stdlib.h> #include <sys/types.h> #include <stdio.h> #include <sys/socket.h> #include <linux/in.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUF_SIZE 1024*5 //#define TCP_PACK_DEBUG 1 int main() { int nBufuseLen=0; //緩沖區里用到的長度 int nRevOnceLen=0; //read 一次讀到的長度 int nPackLen=0; //每一包的長度,包括頭和兩個長度和尾字節 int bufSize=BUF_SIZE; unsigned char buf[BUF_SIZE]; //read 的緩存 int i=0; int sfp,nfp; /* 定義兩個描述符 */ struct sockaddr_in s_add,c_add; int sin_size; unsigned short portnum=6000; /* 服務端使用端口 */ printf("Hello,welcome to my server !\r\n"); sfp = socket(AF_INET, SOCK_STREAM, 0); if(-1 == sfp) { printf("socket fail ! \r\n"); return -1; } printf("socket ok !\r\n"); /* 填充服務器端口地址信息,以便下面使用此地址和端口監聽 */ bzero(&s_add,sizeof(struct sockaddr_in)); s_add.sin_family=AF_INET; s_add.sin_addr.s_addr=htonl(INADDR_ANY); /* 這里地址使用全0,即所有 */ s_add.sin_port=htons(portnum); /* 使用bind進行綁定端口 */ if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr))) { printf("bind fail !\r\n"); return -1; } printf("bind ok !\r\n"); /* 開始監聽相應的端口 */ if(-1 == listen(sfp,5)) { printf("listen fail !\r\n"); return -1; } printf("listen ok\r\n"); // while(1) { // char buf[1027]; // int readlen; sin_size = sizeof(struct sockaddr_in); /* accept服務端使用函數,調用時即進入阻塞狀態,等待用戶進行連接,在沒有客戶端進行連接時,程序停止在此處, 不會看到后面的打印,當有客戶端進行連接時,程序馬上執行一次,然后再次循環到此處繼續等待。 此處accept的第二個參數用於獲取客戶端的端口和地址信息。 */ nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size); if(-1 == nfp) { printf("accept fail !\r\n"); return -1; } printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port)); /* 這里使用write向客戶端發送信息,也可以嘗試使用其他函數實現 */ if(-1 == write(nfp,"hello,welcome to my server \r\n",32)) { printf("write fail!\r\n"); return -1; } //頭0xF9 兩個長度字節 數據 尾0xF8 while(1) { #ifdef TCP_PACK_DEBUG { printf("bufSize = %d ,nBufuseLen = %d\n",bufSize,nBufuseLen); } #endif if(bufSize>nBufuseLen) { nRevOnceLen=read(nfp,buf+nBufuseLen,bufSize-nBufuseLen); nBufuseLen=nBufuseLen+nRevOnceLen; #ifdef TCP_PACK_DEBUG { printf("nBufuseLen = %d>3\n",nBufuseLen); } #endif /* printf(" nRevOnceLen data:\n"); for(i=0; i<nRevOnceLen; i++) { printf(" 0x%x ",buf[i]&0xFF); } printf(" \n"); */ } else { printf("buf if full\n"); } while(nBufuseLen>3) //用到的大於3個字節 { /* printf(" nBufuseLen data:\n"); for(i=0; i<nBufuseLen; i++) { printf(" 0x%x ",buf[i]&0xFF); } printf(" \n"); */ #ifdef TCP_PACK_DEBUG { printf("buf[0] = 0x%x \n",buf[0]&0xFF); } #endif if((buf[0]&0xFF)==0xF9) //頭 { nPackLen=(buf[1]*256)+buf[2]; #ifdef TCP_PACK_DEBUG { printf("nBufuseLen=%d ,nPackLen=%d\n",nBufuseLen,nPackLen); } #endif if(nBufuseLen>=nPackLen) //大於和= { #ifdef TCP_PACK_DEBUG { printf("buf[nPackLen-1] = 0x%x \n",buf[nPackLen-1]&0xFF); } #endif if((buf[nPackLen-1]&0xFF)==0xF8) //尾 { //找到一包數據,所有的數據都往前移動nPackLen個字節 #ifdef TCP_PACK_DEBUG { printf("head 0x%x length %d tail 0x%x \n", buf[0]&0xFF,nPackLen,buf[nPackLen-1]&0xFF); } #endif for(i=0; i<nBufuseLen-nPackLen; i++) { buf[i]=buf[nPackLen+i]; } nBufuseLen=nBufuseLen-nPackLen; //用到的字節變小了 // continue; //不要read了 } else { #ifdef TCP_PACK_DEBUG printf("buf[%d-1]=0x%x,(buf[0]!=0xF8)\n",nPackLen,(buf[nPackLen-1]!=0xF8)); //這里應該不會執行 for(i=0; i<5; i++) { printf(" 0x%x ",buf[i]&0xFF); } printf(" \n"); #endif printf(" (buf[nPackLen-1]&0xFF)!=0xF8 \n"); nBufuseLen=0; //清除錯誤的包 //這里應該不會執行 break; } } else { #ifdef TCP_PACK_DEBUG printf(" nBufuseLen<=nPackLen\n"); #endif break; } } else { printf(" (buf[0]!=0xF9)\n"); //這里應該不會執行 nBufuseLen=0; //清除錯誤的包 //這里應該不會執行 break; } } /* if((0xFF&buf[0])!=0xF2) { printf("readlen = %d buf 0x%x 0x%x 0x%x \n",readlen,0xFF&buf[0],0xFF&buf[1],0xFF&buf[2]); } if(readlen!=1027) printf("readlen = %d buf 0x%x 0x%x 0x%x \n",readlen,0xFF&buf[0],0xFF&buf[1],0xFF&buf[2]); */ } // close(nfp); } // close(sfp); return 0; }