Windows Socket开发之TCP

Sockets_server.cpp

初始化套结字动态库WSAStartup()->创建套接字socket()->绑定套接字bind()->监听(listen())->接受客户端请求accept()->接收客户端数据recv()->关闭套接字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 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;
}


你可能感兴趣的:(Windows Socket开发之TCP)