C++socket引擎开发(一)(待续)

知识点:

  1. #define WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN

#include<Windows.h>
#include<WinSock2.h>


// 作用:"#define WIN32_LEAN_AND_MEAN"尽量避免引用windows.h中早期的定义的宏
  1. 配置一个清爽的开发目录
    项目 属性>常规 选中所有配置、所有平台中的输出目录、中间目录
    在这里插入图片描述值为:
$(SolutionDir)../bin/$(Platform)/$(Configuration)
$(SolutionDir)../temp/$(Platform)/$(Configuration)/$(ProjectName)
  1. 引入ws2_32.lib的静态链接库
    方法一:
    windows平台下使用宏:
	#pragma comment(lib,"ws2_32.lib")//缺点:不能跨平台	

方法二:
在项目属性中 链接器>输入>附加依赖项 中进行添加即可,为一般做法、推荐使用
在这里插入图片描述

代码进度

  1. helloSocket
// Test.cpp
#define WIN32_LEAN_AND_MEAN

#include<Windows.h>
#include<WinSock2.h>

// #pragma comment(lib,"ws2_32.lib")// 需要ws2_32.lib的静态链接库(只能在windows平台下使用)
int main() 
{
	WORD ver = MAKEWORD(2,2);
	WSADATA dat;
	WSAStartup(ver, &dat);// 启动windows的socket的网络环境; 调用了windows的动态库
	///
	
	///
	WSACleanup();// 关闭
	return 0;
}

注:以上代码目前基本不需要理解,为windows环境下socket配置的固定操作。

  1. EasyTcpServer
    简易的可以接收客户端的socket。
    注意事项inet_addr是老函数,高版本VS在编译时默认使用了新函数,所以会报该错误。

    参考文章:
    https://blog.csdn.net/gongxun1994/article/details/83418268

    也可以通过错误提示中的定义宏的方式来解决

    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    #define WIN32_LEAN_AND_MEAN
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    #include<Windows.h>
    #include<WinSock2.h>
    #include<stdio.h>
    
    // #pragma comment(lib,"ws2_32.lib")// 需要ws2_32.lib的静态链接库(只能在windows平台下使用)
    int main()
    {
    	WORD ver = MAKEWORD(2, 2);
    	WSADATA dat;
    	WSAStartup(ver, &dat);// 启动windows的socket的网络环境; 调用了	windows的动态库
    	///
    
    	// 1 创建一个socket
    	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
    	// 2 绑定端口
    	// SOCKET_ERROR
    	sockaddr_in _addr = {};
    	_addr.sin_family = AF_INET;
    	_addr.sin_port = htons(3456);// host to net  unsigned short
    	_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 	inet_addr("127.0.0.1"); // INADDR_ANY;// htons() //注:使用		inet_addr("127.0.0.1")报错。// inet_addr("127.0.0.1")太老
    	if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_addr, sizeof(sockaddr_in)))
    	{
    		printf("Error,绑定端口失败\n");
    	}
    	else
    	{
    		printf("绑定端口成功\n");
    	}
    	// 3 监听网络端口
    	if (SOCKET_ERROR == listen(_sock, 5))
    	{
    		printf("Error,监听网络端口失败");
    	}
    	else
    	{
    		printf("监听网络端口成功");
    	}
    
    	// 	4 等待接收
    	sockaddr_in _clientAddr = {};
    	int nAddrLen = sizeof(sockaddr_in);
    	SOCKET _cSock = INVALID_SOCKET;// 定义一个无效的socket
    	char msgBuf[] = "Hello, I'm Server.";
    
    	while (true)
    	{
    		_cSock = accept(_sock, (sockaddr*)&_clientAddr, &nAddrLen);// 第三个是传入结构的长度
    		if (INVALID_SOCKET == _cSock)// 大规模的链接中可能出现无效的socket链接,一般的小程序基本不会出现
    		{
    			printf("错误,接收到无效的客户端socket...\n");
    		}
    		printf("新客户端加入:IP = %s \n", inet_ntoa(_clientAddr.sin_addr));
    		// 5 向客户端发送一条数据
    	
    		send(_cSock, msgBuf, strlen(msgBuf) + 1, 0);// strlen(msgBuf)+1 把结尾符一并发出去,因为计算长度的时候不会计算结尾符  ???啥意思
    	}
    	// 6 关闭
    	closesocket(_sock);
    
    	///
    // 清除windows socket环境
    	WSACleanup();
    	return 0;
    }
    
    
    
  2. EasyTcpClient
    代码如下

//client.cpp
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS // 或者将sdl选择关闭

#include<Windows.h>
#include<WinSock2.h>
#include<stdio.h>

// #pragma comment(lib,"ws2_32.lib")// 需要ws2_32.lib的静态链接库(只能在windows平台下使用)
int main()
{
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);// 启动windows的socket的网络环境; 调用了windows的动态库
	///
	// 1 建立一个socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET==_sock)
	{
		printf("Error,建立Socket失败\n");
	}
	else
	{
		printf("建立Socket成功...\n");
	}
	// 2 连接服务器
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;// ipv4
	_sin.sin_port = htons(3456);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
	if (SOCKET_ERROR == ret)
	{
		printf("Error,连接服务端失败\n");
	}
	else
	{
		printf("连接服务端成功...\n");
	}

	// 3 接收服务器信息 recv
	char recvBuf[256] = {};
	int nlen= recv(_sock, recvBuf, 256, 0); // 返回数据的长度
	if (nlen>0)
	{
		printf("接收到的数据:%s\n", recvBuf);

	}
	// 4 关闭Socket
	closesocket(_sock);

	///
	WSACleanup();// 关闭

	getchar();
	return 0;
}
  1. 使用SVN管理我们的项目
    建立良好的代码管理习惯
  2. 使用struct来传输数据
  3. 使用网络数据报文来传输数据
    定义:
    网络数据报文有两个部分,包头和包体,是网络消息的基本单元
    包头:描述本次消息包的大小,描述数据的作用
    包体:数据
  4. 将服务端升级到select模型
    select参数:
select(
    _In_ int nfds,
    _Inout_opt_ fd_set FAR * readfds,
    _Inout_opt_ fd_set FAR * writefds,
    _Inout_opt_ fd_set FAR * exceptfds,
    _In_opt_ const struct timeval FAR * timeout
    );
/*
    nfds:在windows平台下无意义,但是在Linux、MacOS、Unix下有意义
    _In:只能被传入不能被改变    
*/
---------------------------------------------------
#ifndef FD_SETSIZE
#define FD_SETSIZE      64
#endif /* FD_SETSIZE */

typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;
/*
默认FD_SETSIZE为64
*/
---------------------------------------------------
#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
---------------------------------------------------
if (FD_ISSET(_sock, &fdRead))// 是否有_sock可读
{
}
---------------------------------------------------
// 相关的一些操作
// 伯克利 socket ??不知道啥是伯克利 socket
fd_set fdRead;
FD_ZERO(&fdRead);// 清空
FD_SET(_sock, &fdRead);// 设置
FD_CLR(_sock, &fdRead);// 清除
timeval t = {10,0}; // 最大阻塞时间
int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, &t);
  1. 为client添加线程 c++标准线程库
#include<thread>

void cmdThread()
{
	char cmdBuf[256]={};
	scanf("%s",cmdBuf);
	if(0==strcmp(cmdBuf,"exit"))
	{
		// 略
	}	
}
int main()
{
// 启动线程
std::thread t1(cmdThread,_sock);
t1.detach(); // 与主线程分离
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值