【windows socket+TCP伺服器客戶端】

Love_Hulidear發表於2014-05-05

Windows Socket+TCP伺服器客戶端


     Winsock是 Windows下套接字標準
         Socket套接字基於計算機網路,提供同一系統上不同程式由區域網連線在一起的不同機器上的程式間通訊功能。如下圖:

套接字通過IP地址Port埠號標識,通過這個標識可以在整個區域網定位一個套接字,通過套接字程式便可以相互傳輸資料。如:程式A與程式B之間欲通過套接字通訊,首先程式A建立一個有IP地址,埠號唯一標識的套接字,程式B同樣建立一個有IP地址,埠號唯一標識的套接字,程式A,B便可以通過對方套接字傳送與接收資訊。
        TCP提供是可靠的資料傳輸服務,通過TCP套介面函式使用。
         1.庫支援

          Winsock API 函式由WS2_32.DLL支援,可通過WS2_32.LIB訪問。Windows socket程式設計前需要初始化WS2_32.DLL,通過han函式WSAStartup完成初始化。
         #include <winsock2.h> 
         #pragma comment(lib,"ws2_32.lib")  //靜態載入ws2_32.lib
WS2_32.DLL 初始化
         int  WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
         該函式第一個引數為WS2_32.DLL版本,通常設為MAKEWORD(2,0),第二個引數為一個指向WSADA他的指標,用於返回WS2_32.DLL的配置資訊。Socket程式設計前需要呼叫該函式。
WS2_32.DLL 釋放
         int  WSACleanup( );
         該函式用於釋放WS2_32.DLL,不需要Winsock功能時呼叫該函式。
        
         2.TCP基本函式

建立socket
         SOCKET  socket (int af, int type, int protocol);
         第一個引數af:  表示族地址,網路程式設計一般使用AF_INET巨集。
         第二個引數type:  表示連線型別,TCP選面向連線SOCK_STREAM,UDP選資料包SOCK_DGRAM。
         第三個引數protocol:  表示協議,使用AF_INET族地址TCP連線時,設為IPPROTO_TCP。
繫結socket
         int  bind(SOCKET s, const struct sockaddr *saddr,int namelen);
         第一個引數:  需要繫結的socket。
         第二個引數:  對應AF_INET,使用struct sockaddr_in包含協議,IP,埠等資訊。
         第三個引數:  對應AF_INET,使用struct sockaddr_in結構大小。
         該函式將socket與協議,IP,埠號繫結起來,相對於給socket“命名”唯一的標識,這樣其他的程式就可以通過這個標識找到這個socket。
監聽socket
          int  listen(SOCKET s,  int nQueueSize);
          第一個引數:  監聽的socket。
          第二個引數:  套接字監聽佇列最大連線請求數。
          該函式將監聽對socket的連線請求。
請求連線
          int  connect(SOCKET s,const struct sockaddr *saddr,int namelen) ;
          第一個引數:  socket本地程式的socket。
          第二個引數:  對應AF_INET,對方IP,埠等socket地址標識sockaddr_in。
          第三個引數:  對應AF_INET,使用sockaddr_in結構大小。
接受連線請求
          SOCKET  accept(SOCKET s,  struct sockaddr  *addr,  int  *addrlen);
          第一個引數:  socket為被監聽的socket。
          第二個引數:  對應AF_INET,一個sockaddr指標,將寫入傳送請求方的sockaddr_in資訊。
          第三個引數:  對應AF_INET,sockaddr_in結構體的大小。
          該函式用於接受一個socket連線請求,返回一個新的連線socket(實質是請求方的socket),傳送與接收資料通過這個連線套socket。
傳送資料
          int  send(SOCKET s, const char *buf,  int len, int flags );
          第一個引數:  socket為對方的socket。
          第二個引數:  傳送資料的緩衝區。
          第三個引數:  資料緩衝區大小。
          第四個引數:  緊急狀態,一般這為0。
          該函式用於向對方socket傳送資料,成功返回傳送資料的大小數。
傳送資料
          int  recv(SOCKET s, char *buf,  int len,  int flags);
          第一個引數:  socket為對方的socket。
          第二個引數:  接收資料的緩衝區。
          第三個引數:  緩衝區大小。
          第四個引數:  緊急狀態,一般這為0。
          該函式用於接收對方傳送的資料,成功返回傳送資料的大小數。
關閉套接字
          int  closesocket(SOCKET s);
          引數為socket。
          該函式用於關閉套接字。

              3.牛刀小試

VC6.0中,先執行TCP伺服器程式,再開啟一個VC6.0,執行TCP客戶端程式。


執行效果:


伺服器端程式:

#include 
#include 
#pragma comment(lib,"ws2_32.lib")
int main()
{
	SOCKET serversoc;   
	SOCKET clientsoc;
	SOCKADDR_IN serveraddr;
	SOCKADDR_IN clientaddr;
	char buf[1024];
	int len;

    WSADATA wsa;
	WSAStartup(MAKEWORD(2,0),&wsa);	//初始化WS2_32.DLL

	//建立套接字
	if((serversoc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 0)  
	{
		printf("套接字socket建立失敗!\n");
		return -1;
	}
	
	//命名協議,IP,埠
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(9102);
	serveraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    
	//繫結套接字
	if(bind(serversoc, (SOCKADDR *)&serveraddr, sizeof(serveraddr)) != 0)
	{
		printf("套接字繫結失敗!\n");
		return -1;
	}
	
	printf("開始監聽...\n");
	//監聽請求
	if(listen(serversoc, 1) != 0)
	{
		printf("監聽失敗!\n");
		return -1;
	}

	len = sizeof(SOCKADDR_IN);
	
	//接收請求
	if((clientsoc = accept(serversoc, (SOCKADDR *)&clientaddr, &len))<=0)
	{
		printf("接受連線失敗!\n");
		return -1;
	}
	printf("連線成功\n");

	//接收資料
	while(1)
	{
		if(recv(clientsoc, buf, 1024, 0) <= 0)	
		{
			printf("關閉連線!\n");
			closesocket(clientsoc);
		}
		printf("接收來自客戶端的資訊: %s\n",buf);
		break;
	}

	//傳送資料
	printf("請輸入傳送給客戶端的字元:\n");
	scanf("%s", buf);
	//send to client
	if(send(clientsoc, buf, strlen(buf)+1, 0)<=0)
	{
		printf("傳送錯誤!\n");
	}
	WSACleanup();     //釋放WS2_32.DLL
	return 0;
}
客戶端程式:
#include 
#include 
#pragma comment(lib,"ws2_32.lib")
int main()
{
	SOCKET clientsocket;
	SOCKADDR_IN serveraddr;
	SOCKADDR_IN clientaddr;
	char buf[1024];

    WSADATA wsa;
	WSAStartup(MAKEWORD(2,0),&wsa);	//初始化WS2_32.DLL
    
	//建立套接字
	if((clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 0)
	{
		printf("套接字socket建立失敗!\n");
		return -1;
	}

	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(9102);
	serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    
	//請求連線
	printf("嘗試連線中...\n");
	if(connect(clientsocket, (SOCKADDR *)&serveraddr, sizeof(serveraddr)) != 0)
	{
		printf("連線失敗!\n");
		return -1;
	}
	printf("連線成功!\n");
	
	//傳送資料
	printf("請輸入傳送給伺服器的字元:\n");
	scanf("%s", buf);
	if(send(clientsocket, buf, strlen(buf)+1, 0)<=0)
	{
		printf("傳送錯誤!\n");
	}
    
	//接收資料
	while(1){
		if(recv(clientsocket, buf, 1024, 0) <= 0)	
		{
			printf("關閉連線!\n");
			closesocket(clientsocket);
		}
		printf("接收來自伺服器的資訊: %s\n",buf);
        break;
	}
    //關閉套接字
    closesocket(clientsocket);
	WSACleanup();    //釋放WS2_32.DLL
	return 0;
}

相關文章