1. 程式人生 > >利用WinSocket發資料(一)

利用WinSocket發資料(一)

幫朋友寫的一個利用winsocket發資料的兩段程式碼.本指望可以通過socket來走低層一些的資料互動,沒想到還是被封殺。沒想到竟然直接檢測socket.就沒用這種思路往下做下去了;

不過這個過程中也對Winsocket有了一定了解;發出來給有需要的朋友;

計劃是利用socket 走tcp協議,然後傳輸一個特定檔案到伺服器段,然後伺服器段接受這個檔案,整個過程有CRC32校驗資料完整性,程式碼在內網測試是ok,的,外圍一開始就被殺掉了。fuck.

其他不多說了。程式碼有詳細解釋。

傳送端程式:

#include "stdafx.h"
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#pragma comment(lib,"ws2_32.lib")  //必須引入的靜態庫  windows 系統會自帶;
#define DEFAULT_PORT    5019 // 埠
#define SEND_BUFFER_LENTH 1024  //傳送的buffer資料長度不包括crc32和length
#define REVE_BUFFER_LENTH 100   //服務端反饋過來的大小
int main(int argc, char **argv){

	int again=1;
	int msg_len=0;//傳送實際len
	int msg_reve_len = 0; //接受實際
	//int addr_len;
	struct sockaddr_in server_addr;
	struct hostent *hp;
	SOCKET connect_sock;
	WSADATA wsaData;
	//檔案流
	fstream walletFile;
	//傳送buffer 
	//長距離傳送必須要做crc16驗證 最後4byte 長度 和放crc16值 4byte 
	char sendBuffer[SEND_BUFFER_LENTH+8]={0};
	char recvBuffer[REVE_BUFFER_LENTH]={0};
	// 伺服器名稱 這個後續可以寫上伺服器的名字 
	//也可以直接指定IP
	char            *server_name = "localhost"; 
	unsigned short    port = DEFAULT_PORT;
	unsigned int    addr;

	//開啟一個檔案 目標  二進位制開啟
	walletFile.open("c:\\test.txt",ios::binary|ios::in|ios::out);
	//先賦初值,清0
	memset(sendBuffer,0,SEND_BUFFER_LENTH+8);
	memset(recvBuffer,0,REVE_BUFFER_LENTH);
	if(!walletFile.good())//檔案不正常開啟
	{
		//關閉檔案
	walletFile.close();
		return 0;
	}
	

	
	//也可以由程式執行時帶入引數,
	//後續考慮那種自由的大一點;
	/*if (argc != 3){
	printf("echoscln [server name] [port number]\n");
	return -1;
	}
	else{
	server_name = argv[1];
	port = atoi(argv[2]);
	}*/

	//初始化套接字型檔
	//失敗暫時關掉,
	//後續可以考慮下多次try catch 

	if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR){				
		return -1;
	}

		
	//得到伺服器 IP資訊
	//通過伺服器名稱來獲得
	hp = gethostbyname(server_name);

	//server_name如果是這種形式:“192.168.1.1”
	//要先把這種字串轉一下
	//addr = inet_addr(server_name);
	//hp = gethostbyaddr((char*)&addr, 4, AF_INET);
	
	//地址沒有解析出來,要退出,
	//後續可以考慮下多次try catch 
	if (hp==NULL)
	{		
		WSACleanup();
		return -1;
	}

	//賦初值 0
	memset(&server_addr, 0, sizeof(server_addr));
	//拷貝解析後的ip
	memcpy(&(server_addr.sin_addr), hp->h_addr, hp->h_length);
	//預設引數
	server_addr.sin_family = hp->h_addrtype;
	//埠
	server_addr.sin_port = htons(port);



	//定義使用TCP套接字連線
	connect_sock = socket(AF_INET,SOCK_STREAM, 0);   

   //
	if (connect_sock == INVALID_SOCKET){	
		WSACleanup();
		return -1;
	}	

	//連線成功
	if (connect(connect_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) 
		== SOCKET_ERROR){		
			WSACleanup();
			return -1;
	}

	//迴圈收發資料開始
	//實際讀取出的大小
	unsigned int realRead = 0;
	unsigned int crcValue = 0;
	do{			
		//一次讀取1024byte
		//賦初值
		memset(sendBuffer,0,SEND_BUFFER_LENTH+8);
		
		walletFile.read(sendBuffer,SEND_BUFFER_LENTH);
			
			//要做資料crc運算 並附加到資料buffer後面
			//這裡做運算的length必須是實際的大小,否則出錯
			//這樣的話就要把每次傳送的長度也封裝咦,因為服務端要做CRC32校驗要長度啊。
			realRead= walletFile.gcount();//取得實際讀取的大小;
			
			//貌似沒必要
			//sendBuffer[realRead]='\0';//手動新增結束符

			crcValue= crc32(sendBuffer,realRead);
			memcpy(&sendBuffer[SEND_BUFFER_LENTH],&realRead,4);//長度寫入
			memcpy(&sendBuffer[SEND_BUFFER_LENTH+4],&crcValue,4); //crc32值
		//}	
		//else{ 
		//	//沒有讀到或者讀完了
		//	break;
		//}
		//發資料 發整個帶有crc16的資料 前面1024是資料,後面兩個byte是crc16值 
		//service 那邊要先檢測crc16的值,不對的話要重發送;
		msg_len = send(connect_sock, sendBuffer,SEND_BUFFER_LENTH+8, 0);

		//msg_len為實際傳送的長度
		if (msg_len == SOCKET_ERROR){			
			WSACleanup();
			again=0;
			return -1;
		}
		//發完了
		if (msg_len == 0){			
			closesocket(connect_sock);
			WSACleanup();
			again=0;
			return -1;
		}
	//從伺服器收返回的資料
		msg_reve_len = recv(connect_sock, recvBuffer,REVE_BUFFER_LENTH, 0);
		if(REVE_BUFFER_LENTH==msg_reve_len){
			//crc32值
			int crcValue = 0;
			int dataLen = 0;
			char receiveCrc32[4]={0};
			char realen[4] = {0};
			for(int ti =0;ti<4;ti++) //取crc32
			{
				receiveCrc32[ti]=recvBuffer[REVE_BUFFER_LENTH-4+ti];
			}
			memcpy(&crcValue,receiveCrc32,4);

			for(int tk =0;tk<4;tk++) //取長度
			{ 
				realen[tk]=recvBuffer[REVE_BUFFER_LENTH-8+tk];
			}
			memcpy(&dataLen,realen,4);
			int dataCrc32 = crc32(recvBuffer,dataLen);
			//fuck 這裡咋整啊,客服端發資料過去有丟失,服務端發過來也有丟失,也要重發,這不死迴圈?
			//待解決的問題啊

			//如果服務端傳回來的資料完好,
			if(dataCrc32==crcValue)
			{
				//如果是resend
				if(recDataCheck(recvBuffer,6,1))
				{
					int timesi = 0;
					//重發這個資料包
					do{
					msg_len = send(connect_sock, sendBuffer,SEND_BUFFER_LENTH+8, 0);
					//收服務端反饋資料

					timesi++;
					}while(msg_len==0&×i<3);
				}
				//如果是sendnext
				if(recDataCheck(recvBuffer,8,1))
				{
				//直接下一個讀取和傳送
					continue;
				}
			}


		}
		else if (msg_reve_len == SOCKET_ERROR){	//異常 		
			closesocket(connect_sock);
			WSACleanup();
				again=0;
			return -1;
		}
		else if (msg_len == 0){		//服務端發了0	
			closesocket(connect_sock);
			WSACleanup();
				again=0;
			return -1;
		}	
		

	}while(again==1&&!walletFile.eof());

	//掃尾工作
	//關閉檔案
	walletFile.close();
	closesocket(connect_sock);
	WSACleanup();
}


unsigned int crc32(char* InStr,unsigned int len){     
    //生成Crc32的查詢表   
    unsigned int Crc32Table[256];   
    int i,j;     
    unsigned int Crc;     
    for (i = 0; i < 256; i++)  
    {     
        Crc = i;     
        for (j = 0; j < 8; j++)  
        {     
            if (Crc & 1)     
                Crc = (Crc >> 1) ^ 0xEDB88320;          
            else    
                Crc >>= 1;   
        }     
        Crc32Table[i] = Crc;     
    }     
    //開始計算CRC32校驗值   
    Crc=0xffffffff;     
    for(int i=0; i<len; i++){    
        Crc = (Crc >> 8)^ Crc32Table[(Crc & 0xFF) ^ InStr[i]];     
    }  
      
    Crc ^= 0xFFFFFFFF;  
    return Crc;     
} 

//接受結果檢測
//1 為 重發檢測 其他為 是否ok檢測
bool recDataCheck(char * receiveData,int receiveLength,int _type)
{
	if(1==_type)
		return needResend(receiveData,receiveLength);
	else
		return sendOk(receiveData,receiveLength);
}

//檢測是否是重發浮標
bool needResend(char * receiveData,int receiveLength)
{
		char* reSend ="resend";
		try{
			for(int i = 0;i<receiveLength;i++)
			{
				if(reSend[i]!=receiveData[i])
					return false;
			}
		}
		catch(...)
		{
			return false;
		}
		return true;
}
//檢測服務短 收過來的是不是已接收ok
bool sendOk(char * receiveData,int receiveLength)
{
	char* sendOk ="sendnext";
	try{
			for(int i = 0;i<receiveLength;i++)
			{
				if(sendOk[i]!=receiveData[i])
					return false;
			}
		}
		catch(...)
		{
			return false;
		}
		return true;

}