Windows Socket開發之TCP

Sockets_server.cpp
編程

初始化套結字動態庫WSAStartup()->建立套接字socket()->綁定套接字bind()->監聽(listen())->接受客戶端請求accept()->接收客戶端數據recv()->關閉套接字closesocket()->釋放套接字資源WSACleanup()windows

#include "stdafx.h"
#define BUF_SIZE 64
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsd;			//存放windows socket初始化信息
	SOCKET sServer;			//服務端套接字
	SOCKET sClient;			//客戶端套接字
	SOCKADDR_IN addrServ;	//服務器地址
	char buf[BUF_SIZE];		//接收數據緩衝區
	int retVal;
	/*
	#define MAKEWORD(a,b) ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
	makeword是將兩個byte型合併成一個word型,一個在高8位(b),一個在低8位(a)。
	在WSAStartup裏面,MAKEWORD(2, 2)指Windows Sockets版本號,高位字節指出副版本(修正)號,低位字節指明主版本號。
	*/
	//初始化套接字動態庫
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		printf("WSAStartup failed!\n");
		return 1;
	}

	/*
	AF 表示ADDRESS FAMILY 地址族,PF 表示PROTOCOL FAMILY 協議族,
	但這兩個宏定義是同樣的,因此使用哪一個都沒有關係。
	Winsock2.h中#define AF_INET 2,#define PF_INET AF_INET,
	因此在windows中AF_INET與PF_INET徹底同樣。而在Unix/Linux系統中,
	在不一樣的版本中這二者有微小差異。對於BSD,是AF,對於POSIX是PF。
	UNIX系統支持AF_INET,AF_UNIX,AF_NS等,而DOS,
	Windows中僅支持AF_INET,它是網際網區域。
	PF_INET, AF_INET: Ipv4網絡協議; 
	PF_INET6, AF_INET6: Ipv6網絡協議。
	*/
	/*
	IPPROTO_TCP:指使用tcp協議, #define IPPROTO_TCP 6 // tcp。
	*/
	/*
	SOCK_STREAM:提供面向鏈接的穩定數據傳輸,即TCP協議。
	*/
	//建立套接字
	// 第一個參數,指定地址簇(TCP/IP只能是AF_INET,也可寫成PF_INET)  
        // 第二個,選擇套接字的類型(流式套接字)
        // 第三個,特定地址家族相關協議(0爲自動)
	sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sServer)
	{
		printf("socket failed!\n");
		WSACleanup();//釋放套接字資源
		return -1;
	}
	/*
	htons在Windows和Linux網絡編程時須要用到的,用來將主機字節順序轉化爲網絡字節順序,
	功能是將一個無符號短整型的主機數值轉換爲網絡字節順序,即大尾順序(big-endian),即高位字節存放在內存的低地址處。
	網絡字節順序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操做系統等無關,從而能夠保證數據在不一樣主機之間傳輸時可以被正確解釋,網絡字節順序採用big-endian排序方式。
	咱們經常使用的 x86 CPU (intel, AMD) 電腦是 little-endian,也就是整數的低位字節放在內存的低字節處。
	而little-endian指,地址的低位存儲值的低位。
	*/
	//設置服務器套接字地址
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(4999);
	addrServ.sin_addr.s_addr = INADDR_ANY;

	// 套接字sockSrv與本地地址相連  
        // int bind(SOCKET s, const struct sockaddr* name, int namelen);  
        // 第一個參數,指定須要綁定的套接字;  
        // 第二個參數,指定該套接字的本地地址信息,該地址結構會隨所用的網絡協議的不一樣而不一樣  
        // 第三個參數,指定該網絡協議地址的長度  
        // PS: struct sockaddr{ u_short sa_family; char sa_data[14];};  
        //                      sa_family指定該地址家族, sa_data起到佔位佔用一塊內存分配區的做用  
        //     在TCP/IP中,可以使用sockaddr_in結構替換sockaddr,以方便填寫地址信息  
        //   
        //     struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr si                n_addr; char sin_zero[8];};  
        //     sin_family表示地址族,對於IP地址,sin_family成員將一直是AF_INET。  
        //     sin_port指定將要分配給套接字的端口。  
        //     sin_addr給出套接字的主機IP地址。  
        //     sin_zero[8]給出填充數,讓sockaddr_in與sockaddr結構的長度同樣。  
        //     將IP地址指定爲INADDR_ANY,容許套接字向任何分配給本地機器的IP地址發送或接收數據。  
        //     若是想只讓套接字使用多個IP中的一個地址,可指定實際地址,用inet_addr()函數。  
	retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
	if (SOCKET_ERROR == retVal)
	{
		printf("bind failed!\n");
		closesocket(sServer);//關閉套接字
		WSACleanup();
		return -1;
	}

	// 將套接字設置爲監聽模式(鏈接請求), listen()通知TCP服務器準備好接收鏈接  
        // int listen(SOCKET s,  int backlog);  
        // 第一個參數指定須要設置的套接字,第二個參數爲(等待鏈接隊列的最大長度)  
	retVal = listen(sServer, 1);
	if (SOCKET_ERROR == retVal)
	{
		printf("listen failed!\n");
		closesocket(sServer);
		WSACleanup();
		return -1;
	}

	//接受客戶端請求
	sockaddr_in addrClient;
	int addrClientlen = sizeof(addrClient);
	
	// 接收鏈接,等待客戶端鏈接  
        // SOCKET accept(  SOCKET s,  struct sockaddr* addr,  int* addrlen); 
	// 第一個參數,接收一個處於監聽狀態下的套接字  
        // 第二個參數,sockaddr用於保存客戶端地址的信息  
        // 第三個參數,用於指定這個地址的長度  
        // 返回的是向與這個監聽狀態下的套接字通訊的套接字  
	sClient = accept(sServer, (sockaddr FAR*)&addrClient, &addrClientlen);
	if (INVALID_SOCKET == sClient)
	{
		printf("accept failed!\n");
		closesocket(sServer);
		WSACleanup();
		return -1;
	}

	//接收客戶端數據
	ZeroMemory(buf, BUF_SIZE);
	retVal = recv(sClient, buf, BUF_SIZE, 0);
	if (SOCKET_ERROR == retVal)
	{
		printf("recv failed!\n");
		closesocket(sServer);
		closesocket(sClient);
		WSACleanup();
		return -1;
	}
	printf("%s\n", buf);

	//關閉套接字
	closesocket(sServer);
	closesocket(sClient);
	//釋放套接字資源
	WSACleanup();

	return 0;
}

Sockets_Client.cpp服務器

初始化套結字動態庫WSAStartup()->建立套接字socket()->鏈接服務器connect()->向服務器發送數據send()->關閉套接字closesocket()->釋放套接字資源WSACleanup()網絡

#include "stdafx.h"
#define BUF_SIZE 64
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")


int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsd;			//存放windows socket初始化信息
	SOCKET sHost;			//服務器套接字
	SOCKADDR_IN servAddr;	        //服務器地址
	char buf[BUF_SIZE];		//接收數據緩衝區
	int retVal;	

	//初始化套結字動態庫
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		printf("WSAStartup failed!\n");
		return -1;
	}

	//建立服務器套接字
	sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sHost)
	{
		printf("socket failed!\n");
		WSACleanup();
		return -1;
	}

	//設置服務器地址
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servAddr.sin_port = htons((short)4999);
	int nServAddlen = sizeof(servAddr);

	//鏈接服務器
	retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
	if (SOCKET_ERROR == retVal)
	{
		printf("connect failed!\n");
		closesocket(sHost);
		WSACleanup();
		return -1;
	}

	ZeroMemory(buf, BUF_SIZE);
	strcpy_s(buf, "MyTCP");

	//向服務器發送數據
	// 第一個參數,須要發送信息的套接字,  
        // 第二個參數,包含了須要被傳送的數據,  
        // 第三個參數是buffer的數據長度,  
        // 第四個參數,一些傳送參數的設置  
	retVal = send(sHost, buf, strlen(buf), 0);
	if (SOCKET_ERROR == retVal)
	{
		printf("send failed!\n");
		closesocket(sHost);
		WSACleanup();
		return -1;
	}

	//關閉套接字和釋放資源
	closesocket(sHost);
	WSACleanup();
	return 0;
}
相關文章
相關標籤/搜索