麻将网络模块分析2

本文探讨了麻将网络模块中单个Socket的实现,包括设置服务器地址、端口、发送内容、发送长度等关键步骤。同时,介绍了如何构建网络回调以及Socket的关闭操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CMyNetModual.h
//此模块是用来创建socket与服务器通信用的
#ifndef __MY_NET_MODUAL_H__
#define __MY_NET_MODUAL_H__
#include "inc.h"


#define TCP_MODE	1
#define UDP_MODE	2
#define HTTP_GET_MODE	3	//get 方式
#define HTTP_POST_MODE	4	//post 方式


enum HTTP_STATE_ENUM
{
	NET_TIME_OUT = -7,
	NET_CON_ERROR = -6,
	NET_CREATE_ERROR = -5,
	NET_READ_ERROR = -4,	//读取数据错误
	NET_WRITE_ERROR = -3,	
	NET_CLOSE = -2,			//服务器断开网络
	NET_ERROR = -1,
	NET_CON_SUCCESS = 0,
	NET_CON_ING,
	NET_CON_WOULDBLOCK,	//非阻塞标记
	NET_SEND_HEAD,
	NET_SEND_POST,
	NET_READ_HEAD,
	NET_READ,
	NET_WRITE,
	NET_FINISH,
};

//暂时定为30秒(一秒跑的次数)*30(秒)
#define NETOUTTIMES	(60*30)

//读取数据大小
#define  NET_BUF_LEN	(1024*4)


//定义回调函数指针
//event:事件  index:标志 buf:数据 len:数据长度
//如果收到NET_SEND_POST消息则传NET_DATA_STRUCT(要发送的数据和长度)填充到此指针中
typedef void (*NET_CALLBACK)( int event, int index, void* buf, int len );

//socket的管理类,主要实现创建哪种类型的socket,连接,发送数据,接收数据等
class CMyNetModual
{
public:
	//http的方式联网 默认是80  retry默认传0
	CMyNetModual(char* Address, int port, int retry, NET_CALLBACK callback ,char* postContent, int postlen );
	//socket方式联网  mode = 1表示TCP  mode = 0表示UDP  端口默认是80 方式默认是TCP 重试次数默认是3次
	CMyNetModual(char* Address, int port, int mode, int retry,  NET_CALLBACK callback,char* postContent, int postlen );
	~CMyNetModual();

	void init(void);


	//socket操作
	int Create(void);
	int Close(void);
	int Connect(void);	
	int Send( void *data, int len );
	int Recv( void *data, int len );
	int IsConnectError(void);


public:
	//网络返回状态200 206是正常其它是错误 -1是没有接收到头
	int net_http_status;
	//网络状态
	int net_status;
	//0: UDP, 1: TCP 2:http
	int net_mode;
	//sock索引
	int net_index;
	//联网失败重试次数
	int net_tolretry;
	//当前连接次数
	int net_curretry;
	//ip地址
	char net_ip[32];
	int net_port;
	//url
	char net_url[256];
	//时间记录
	int timecount;
	//sockid句柄
	int net_handle;

	//接收头数据  post发送的时候用来存储发送的字节数
	char net_headbuf[NET_BUF_LEN];

	//要接收的数据长度 用于HTTP
	int net_total_len;
	//已接收的数据长度 用于HTTP
	int net_dl_len;
	int m_postlen;
	NET_CALLBACK net_callback;

public:
	//分析adress   adress:传进来的地址  host: 主机地址  port: 端口
	//返回值:判断地址是否IP  -1:错误参数 ip:true  or false 
	int AnalizeAdress( char *adress, char* host );

};


#endif

单个socket的实现管

构建一些创建socket的条件,如服务器地址,端口,发送内容,发送长度,以及网络的回调


void CMyNetModual::init()//初始化成员变量
{
	net_status = -1;
	net_http_status = -1;
	net_mode = 1;
	net_index = 0;
	net_tolretry = 3;
	net_curretry = 0;
	memset( net_ip, 0, sizeof(net_ip) );
	memset( net_url, 0, sizeof(net_url) );
	net_port = 0;
	net_callback = 0;
	net_handle = 0;
	timecount = 0;
	net_total_len = 0;
	net_dl_len = 0;
	m_postlen = 0;
}

//构建一些创建socket的条件,如服务器地址,端口,发送内容,发送长度,以及网络的回调
CMyNetModual::CMyNetModual(char* Address, int port, int retry, NET_CALLBACK callback ,char* postContent, int postlen )
{
	init();
	if ( retry > 0 )
	{
		net_tolretry = retry;
	}
	memcpy( net_url, Address, sizeof(net_url) );
	net_port = port;
	net_callback = callback;
	memset( net_headbuf, 0, NET_BUF_LEN );
	if ( postContent )
	{
		memcpy( net_headbuf, postContent, postlen );
	}
	m_postlen = postlen;
}


CMyNetModual::CMyNetModual(char* Address, int port, int mode, int retry,  NET_CALLBACK callback,char* postContent, int postlen  )
{
	init();
	if ( retry > 0 )
	{
		net_tolretry = retry;
	}
	strcpy( (char*)net_url, Address );
	net_port = port;
	net_callback = callback;
	net_mode = mode;
	memset( net_headbuf, 0, NET_BUF_LEN );
	if ( postContent )
	{
		memcpy( net_headbuf, postContent, postlen );
	}	
	m_postlen = postlen;
}

//关闭socket
CMyNetModual::~CMyNetModual()
{
	if ( net_handle >= 0 )
	{
#ifdef _WIN32
		closesocket( net_handle );
#else
		close( net_handle );
#endif

	}
}

当构建好创建一个socket的必须条件后,他应该有创建socket的方法

int CMyNetModual::Create(void)
{
	int mode = (net_mode == UDP_MODE) ? SOCK_DGRAM:SOCK_STREAM;
	int socketid = socket( AF_INET, mode, 0 ); //得到socket句柄
#ifdef _WIN32
	if( socketid < 0 )
	{
		int error = WSAGetLastError();
		CCLog( "network Create socket error = %d", error );
	}
#endif
	CCLog( "network Create socket id = %d", socketid );
	//memset( net_headbuf, 0, sizeof(net_headbuf) );
	if ( socketid >= 0 )
	{
#ifdef _WIN32
		unsigned long ul = 1;
		ioctlsocket(socketid, FIONBIO, &ul); //设置为非阻塞模式
#else
		int flags, s;
		flags = fcntl (socketid, F_GETFL, 0);
		if (flags == -1)
		{
			close(socketid);
			return -1;
		}

		flags |= O_NONBLOCK;
		s = fcntl (socketid, F_SETFL, flags);
		if (s == -1)
		{
			close(socketid);
			return -1;
		}
#endif
		net_handle = socketid;       //返回socket的句柄
		return net_handle;
	}
	return NET_CREATE_ERROR;
}

其中 win32环境设置和linux环境设置socket的为非阻塞模式不同


socket的关闭操作

int CMyNetModual::Close()
{
	if ( net_handle >= 0 )
	{
#ifdef _WIN32
		closesocket( net_handle );
#else
		close( net_handle );
#endif

	}
	net_handle = -1;
	return 0;
}

socket的连接操作

int CMyNetModual::Connect(void)
{
	struct sockaddr_in sin = {0};
	char host[32] = {0};
	struct hostent* hostmsg = NULL;
	//先获取IP地址
	//重试的时候不用在获取IP地址: strlen( net_ip )
	if ( strlen( net_ip ) == 0 &&  AnalizeAdress( net_url, net_ip ) != 1 )
	{
		//阻塞模式
		CCLog( "network before gethostbyname" );
		hostmsg = gethostbyname( (char*)net_ip );
		if(hostmsg == NULL)
			return NET_CON_ERROR;
		CCLog( "network after gethostbyname" );
		memset( host, 0, sizeof(host) );
#ifdef _WIN32
		struct in_addr addr;
		addr.s_addr = *(u_long *)hostmsg->h_addr_list[0];
		strcpy( net_ip, (char*)inet_ntoa(addr) );
#else
		if( inet_ntop(hostmsg->h_addrtype, (void*)hostmsg->h_addr_list[0], (char*)host, sizeof(host) ) )
		{
			//IP
			memset( net_ip, 0, sizeof(net_ip) );
			memcpy( net_ip, host, strlen(host) );
			CCLog( "network before connect ip = %s-----", net_ip );
		}
		else
		{
			//inet_ntop返回NULL
			CCLog( "network before connect ip = ERROR");
			return NET_ERROR;
		}
#endif
	}

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(net_ip);
	sin.sin_port = htons(net_port);
	CCLog( "network before connect" );
	int nRet = connect( net_handle, (struct sockaddr*)&sin, sizeof(sin) );
	net_status = NET_CON_ING;       //设置当前socket的网络连接状态为连接中
	if ( nRet == -1 )
	{
		//判断是否是阻塞
#ifdef _WIN32
		int errValue = WSAGetLastError();
		CCLog( "network socket connect = -1 errValue= %d", errValue );
		if ( errValue == WSAEWOULDBLOCK )
		{
			return NET_CON_WOULDBLOCK;
		}
		else
		{
			return NET_CON_ERROR;
		}
#else
		int errValue = errno;
		CCLog( "network socket connect errno= %d", errno );
		if ( errValue == EINPROGRESS )
		{
			return NET_CON_WOULDBLOCK;
		}
		else
		{
			return NET_CON_ERROR;
		}
#endif
	}
	CCLog( "network socket connect success ret= %d", nRet );
	return NET_CON_SUCCESS;

}

封装socket的发送操作 linux 和win32 不同

int CMyNetModual::Send( void *data, int len )
{
#ifdef _WIN32
	return send( net_handle, (char*)data, len, 0 );
#else
	if ( data )
	{
		CCLog( "network send data = %s len = %d", (char*)data, len );
	}
	else
	{
		CCLog( "data = NULL");
	}
	int sendlen = send( net_handle, (char*)data, len, MSG_NOSIGNAL );
	CCLog( "network sed len = %d", sendlen );
	return sendlen;
#endif
}

封装socket对象的接收操作

int CMyNetModual::Recv( void *data, int len )
{
	return recv( net_handle, (char*)data, len, 0 );

}

解析主机地址,判断是否为ip地址

//www.xxx.com/xxxxx
//10.0.0.0:xxxx/xxxx
int CMyNetModual::AnalizeAdress( char *adress, char* host )
{
	if ( !adress || !host )
	{
		return -1;
	}
	char *httpflag = strstr( adress, (char*)"http://" );
	//不带http://则指向头
	if ( httpflag == NULL )
	{
		httpflag = adress;
	}
	else
	{
		httpflag += strlen( (char*)"http://" );
	}

	char *endflag = strchr( httpflag, (char)':' );
	//不带/则指向尾
	if ( endflag == NULL )
	{
		endflag = strchr( httpflag, (char)'/' );
		if ( endflag == NULL )
		{
			endflag = httpflag + strlen( httpflag );
		}
	}

	//取主机地址
	memcpy( host, httpflag, endflag - httpflag );

	//判断是否IP
	int adress_len = strlen( host );
	for ( int i = 0; i < adress_len; i++ )
	{
		if ( isdigit(host[i]) == 0 && host[i] != '.' )
		{
			return 0;
		}
	}
	return 1;
}

由此,socket的通信模块已经完成,任何一种通信模式都是一个
CMyNetModual的对象 封装了 创建socket句柄,连接,发送,接收数据等操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值