用select模式收发处理数据(跨平台)

   本Demo程序模拟C/S传输数据。采用select模式,分别实现了一个客户端小程序和服务端小程序。已在VC2005和Fedroa 13下测试通过。因时间有限,功能简单,欢迎交流,学习!

 

main.cpp

#include "Sock.h"

/*
*
* 传输数据格式
*  ___________ _____________
* |           |             |
* | 32字节头   | 负载数据     |
* |___________|_____________|
*
* 其中32位头中:
* 第0个字节为数据包起始标志:0xCC
* 第1个字节为命令字
* 第4个字节为负载数据长度
* 其余字节填0
*
*/

int main(int argc, char* argv[])
{
	int nRet;
	Sock g_sock;
	char szBuf[1024];

	//初始化Socket环境
	nRet = g_sock.StartUp();
	if (nRet < 0)
	{
		cout<<endl<<"main::init system error"<<endl;
		return 1;
	}

	//客户端
#if defined(_CLIENT_)

	cout<<endl<<"please input s, to start send data : ";

	char ch;
	while (1)
	{
		cin>>ch;
		if ( ch == 's')
		{
			break;
		}
	}

	g_sock.SetRemoteAddr(SERVER_IP, SERVER_PORT);

	if (opMode_udp == g_sock.GetProtocol())
	{
		//创建并绑定本端Socket
		nRet = g_sock.CreateSock(CLIENT_IP, CLIENT_PORT);
		if (nRet < 0)
		{
			cout<<endl<<"main::CreateSock error"<<endl;
			return 1;
		}
	}
	else if (opMode_tcp == g_sock.GetProtocol())
	{
		//创建本端Socket,并连接服务端
		nRet = g_sock.StartConnect(CLIENT_IP, CLIENT_PORT, 5000);
		if (nRet < 0)
		{
			cout<<endl<<"main::StartConnect error"<<endl;
			return 1;
		}
	}

	//向服务端发送登录请求
	memset(szBuf, 0, sizeof(szBuf));
	szBuf[0] = (char)0xCC;
	szBuf[1] = (char)0xA0;
	char *p = "Hello Server!";
	int nExtLen = strlen(p);
	memcpy(szBuf + 4, &nExtLen, sizeof(unsigned int));
	strcpy(szBuf + HEADER_SIZE, p);
	nRet = g_sock.SendData(szBuf, HEADER_SIZE + nExtLen);
	if (nRet < 0)
	{
		cout<<endl<<"main::SendData error"<<endl;
	}
	else
	{
		cout<<endl<<"main::SendData ok"<<endl;
	}


	//接收来自服务端的请求回应,并处理
	nRet = g_sock.RecvData();

	//服务端
#elif defined(_SERVER_)
	
	//创建本端套接字,绑定,并开始侦听
	nRet = g_sock.StartListen(SERVER_IP, SERVER_PORT);
	if (nRet < 0)
	{
		cout<<endl<<"main::StartListen error"<<endl;
		return 1;
	}

	g_sock.SetRemoteAddr(CLIENT_IP, CLIENT_PORT);

	//接收来自客户端的请求,并进行应答处理
	nRet = g_sock.RecvData();

#endif

#ifdef WIN32
	system("pause");
#endif

	//清理socket环境
	g_sock.CleanUp();

	return 0;
}


 

sock.h

 

#ifndef _SOCK_H
#define _SOCK_H

#ifdef WIN32

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif

#define    WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>

#define EWOULDBLOCK             WSAEWOULDBLOCK
#define EINPROGRESS             WSAEINPROGRESS

#pragma warning(disable:4996)

#else

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>

#define EWOULDBLOCK EAGAIN

#include <assert.h>

#include <netinet/tcp.h>
#include <semaphore.h>

typedef int SOCKET;
#define INVALID_SOCKET -1
#define closesocket close 

#endif

#include <iostream>
using namespace std;

typedef enum _opMode
{
	opMode_none = 0,
	opMode_tcp,
	opMode_udp
} opMode;

#define HEADER_SIZE 32

#define CLIENT_IP	"127.0.0.1"
#define SERVER_IP	"127.0.0.1"
#define CLIENT_PORT 5678
#define SERVER_PORT 5679


class Sock
{
public:
	Sock(void);
	~Sock(void);

	int SetRemoteAddr(const char* szIp, int nPort);
	int GetProtocol(){return m_nProtocol;}

	int StartUp();
	int CleanUp();

	int CreateSock(const char* szIp = NULL, int nPort = 0);
	int StartListen(const char* szIp, int nPort);
	int StartConnect(const char* szIp, int nPort, int nTimeOut);

	int SendData(char *pszbuf,int nLength);
	int RecvData();
	int TcpRecv();
	int UdpRecv();

	int DealData(char *pData, int nLength);

private:

	unsigned long   m_localIp;
	unsigned short	m_localPort;
	unsigned long	m_remoteIp;	
	unsigned short	m_remotePort;

	int				m_nProtocol;
	SOCKET			m_socket;
	SOCKET			m_clientSock;

	int				m_recvBuffSize;
	int				m_sendBuffSize;

	int				m_bExitRecv;
};


#endif

Sock.cpp

#include "Sock.h"

Sock::Sock(void)
{
	m_remoteIp	= INADDR_ANY;
	m_remotePort= 0;
	m_localIp	= INADDR_ANY;
	m_localPort	= 0;
	m_socket = INVALID_SOCKET;
	m_clientSock = INVALID_SOCKET;

	m_recvBuffSize = 4 * 1024;
	m_sendBuffSize = 4 * 1024;

	m_bExitRecv = false;

	m_nProtocol = opMode_udp;
}

Sock::~Sock(void)
{
}

int Sock::SetRemoteAddr(const char* szIp, int nPort)
{
	m_remoteIp = inet_addr(szIp);
	m_remotePort = htons(nPort);

	return 1;
}

int Sock::StartUp()
{
#ifdef WIN32
	WSADATA wsa = {0};
	int ret = WSAStartup(MAKEWORD(2,2), &wsa);
	if (ret != 0)
	{
		return -1;
	}
#endif
	return 1;
}

int Sock::CleanUp()
{
#ifdef WIN32
	if (WSACleanup() == SOCKET_ERROR)
	{
		return -1;
	}
#endif
	return 1;
}

int	Sock::CreateSock(const char* szIp, int nPort)
{
	if (m_socket != INVALID_SOCKET)
	{
		return -1;
	}

	//创建套接字
	if (opMode_tcp == m_nProtocol)
	{
		m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	}
	else if (opMode_udp == m_nProtocol)
	{
		m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	}
#ifdef WIN32
	errno = WSAGetLastError();
#endif
	if (m_socket == INVALID_SOCKET)
	{
		return -1;
	}

	int nBlock = 1; //设为非阻塞模式
	int nRet;
#ifdef WIN32
	nRet = ::ioctlsocket(m_socket,FIONBIO,(u_long FAR *)&nBlock);
	if (nRet == SOCKET_ERROR) 
	{
		errno = ::WSAGetLastError();
		nRet = -1;
	}
#else
	nBlock = ::fcntl(m_socket, F_GETFL, 0);
	if (nBlock != -1)
	{
		nBlock |= O_NONBLOCK;
		nRet = ::fcntl(m_socket, F_SETFL, nBlock);
	}
#endif
	if ( -1 == nRet )
	{
		closesocket(m_socket); 
		m_socket = INVALID_SOCKET;
		return -1;
	}

	if (szIp == NULL)
	{
		m_localIp = INADDR_ANY;
	}
	else
	{
		m_localIp = inet_addr(szIp);
	}

	m_localPort = htons(nPort);

	//绑定套接字端口
	if (m_localPort != 0)
	{
		struct sockaddr_in local_addr;
		memset(&local_addr, 0, sizeof(struct sockaddr_in));
		local_addr.sin_family = AF_INET;
		local_addr.sin_port = m_localPort;
		local_addr.sin_addr.s_addr = m_localIp;

		if (INVALID_SOCKET == bind(m_socket, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)))
		{
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
			return -1;
		}
	}
	else if (opMode_udp == m_nProtocol)
	{
		//随机绑定
		struct sockaddr_in local_addr;
		memset(&local_addr, 0, sizeof(struct sockaddr_in));
		local_addr.sin_family = AF_INET;

		if ( '\0' == szIp[0] )
		{
			local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		}
		else
		{
			local_addr.sin_addr.s_addr = inet_addr(szIp);
		}

		int n;
		for (n = 0; n < 10000; n++)
		{
			local_addr.sin_port = htons(nPort + n);
			if ( 0 == bind(m_socket, (struct sockaddr*)&local_addr, sizeof(local_addr)) )
			{
				break;
			}
		}

		if (10000 == n)
		{
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
			return -1;
		}

	}

	//设置套接字接收和发送缓冲区大小
	if(m_recvBuffSize > 0)
		setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&m_recvBuffSize, sizeof(int));
	if(m_sendBuffSize > 0)
		setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&m_sendBuffSize, sizeof(int));

	return 1;

}

int Sock::StartListen(const char* szIp, int nPort)
{
	int nRet = CreateSock(szIp, nPort);
	if ( nRet < 0 )
	{
		return -1;
	}

	if (opMode_tcp == m_nProtocol)
	{
		listen(m_socket,5);
	}

	return 1;
}

int Sock::StartConnect(const char* szIp, int nPort, int nTimeOut)
{
	int nRet = CreateSock(szIp, nPort);
	if (nRet < 0)
	{
		return -1;
	}

	struct sockaddr_in remote_addr;
	memset(&remote_addr, 0, sizeof(remote_addr));

	remote_addr.sin_family = AF_INET;
	remote_addr.sin_addr.s_addr = m_remoteIp;
	remote_addr.sin_port = m_remotePort;

	//连接服务端
	nRet = connect(m_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr));
#ifdef WIN32
	if ( SOCKET_ERROR == nRet && (errno = WSAGetLastError()) == EWOULDBLOCK)
	{
#else
	if ( -1 == nRet && EINPROGRESS == errno )
	{
		errno = EWOULDBLOCK; 
#endif

		fd_set fdwrite;
		FD_ZERO(&fdwrite);
		FD_SET(m_socket,&fdwrite);
		timeval tv;
		tv.tv_sec = nTimeOut/1000;
		tv.tv_usec = nTimeOut%1000;
		nRet = select(m_socket + 1, NULL, &fdwrite, NULL, &tv);
#ifdef WIN32
		if ( SOCKET_ERROR == nRet )
		{
			errno = WSAGetLastError();
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
			return -1;
		}
#else
		if ( -1 == nRet )
		{
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
		    return -1;
		}
#endif
		if ( 0 == nRet ) //超时
		{
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
			return -1;
		}
	}
	else
	{
		closesocket(m_socket);
		m_socket = INVALID_SOCKET;
		return -1;
	}

	return 1;
}

int Sock::SendData(char *pszbuf,int nLength)
{
	if (!pszbuf)
	{
		return -1;
	}

	int nRet;
	if (opMode_tcp == m_nProtocol)
	{
#if defined(_CLIENT_)
		nRet = send(m_socket,pszbuf,nLength,0);
#elif defined(_SERVER_)
		nRet = send(m_clientSock,pszbuf,nLength,0);
#endif
	}
	else if (opMode_udp == m_nProtocol)
	{
		sockaddr_in remote_addr;
		remote_addr.sin_family = AF_INET;
		remote_addr.sin_addr.s_addr = m_remoteIp;
		remote_addr.sin_port = m_remotePort;
		nRet = sendto(m_socket,pszbuf,nLength,0,(sockaddr*)&remote_addr,sizeof(remote_addr));
	}

	if (nRet != nLength)
	{
		cout<<endl<<"Sock::SendData socket error.";
		return -1;
	}
	else
	{
		cout<<endl<<"Sock::SendData send data ok.";
	}

	return 1;
}

int Sock::RecvData()
{
	int nRet;
	if (opMode_udp == m_nProtocol)
	{
		nRet = UdpRecv();
	}
	else if (opMode_tcp == m_nProtocol)
	{
		nRet = TcpRecv();
	}

	return 1;
}

int Sock::TcpRecv()
{
	int fds,nRet;
	int nNeeRecvLen = 0,nRecvLen = 0, ntmpLen = 0;
	timeval tv;
	char szbuf[1024*4];
	int iTotal;
	fd_set fd_recv;
	FD_ZERO(&fd_recv);
	sockaddr_in addr;
	int iAddrSize = sizeof(addr);
	SOCKET tmpSock;

	while (true)
	{
		if (m_bExitRecv)
		{
			break;
		}

		tv.tv_sec = 0;
		tv.tv_usec = 100000;
		//memset(szbuf,0,sizeof(szbuf));
		fds = 0;
		FD_ZERO(&fd_recv);
		FD_SET(m_socket,&fd_recv);
		fds = m_socket;
#ifdef _SERVER_
		if (INVALID_SOCKET != m_clientSock)
		{
			FD_SET(m_clientSock,&fd_recv);
			if (m_clientSock > m_socket)
			{
				fds = m_clientSock;
			}
		}
#endif

		if ( fds <=0 )
		{
#ifdef WIN32
			Sleep(1);
#else
			sleep(1);
#endif
			continue;
		}
		iTotal = select(fds + 1, &fd_recv, 0, 0, &tv);

#ifdef WIN32
		if ( SOCKET_ERROR == iTotal )
		{
			errno = WSAGetLastError();
			cout<<endl<<"Sock::TcpRecv socket error = "<<errno;
		}
#else
		if ( -1 == iTotal )
		{
			cout<<endl<<"Sock::TcpRecv socket error = "<<errno;
		}
#endif
		if ( iTotal == 0 ) //超时
		{
			continue;
		}
		if (FD_ISSET(m_socket,&fd_recv) || FD_ISSET(m_clientSock,&fd_recv))//接收
		{
#ifdef _SERVER_
			if (INVALID_SOCKET == m_clientSock)
			{
				m_clientSock = accept(m_socket,(sockaddr*)&addr,
#if !defined(WIN32)
					(socklen_t*)
#endif
					&iAddrSize);
				if ( INVALID_SOCKET == m_clientSock ) //错误
				{
#ifdef WIN32
					errno = WSAGetLastError();
#endif
					cout<<endl<<"Sock::TcpRecv socket error = "<<errno;
				}
				else
				{
					int nRet;
					nRet = setsockopt(m_clientSock, SOL_SOCKET, SO_RCVBUF, (char*)&m_recvBuffSize, sizeof(m_recvBuffSize));
					nRet = setsockopt(m_clientSock, SOL_SOCKET, SO_SNDBUF, (char*)&m_sendBuffSize, sizeof(m_sendBuffSize));
					int nBlock = 1;
#ifdef WIN32
					nRet = ::ioctlsocket(m_clientSock,FIONBIO,(u_long FAR *)&nBlock);
					if (nRet == SOCKET_ERROR) 
					{
						errno = ::WSAGetLastError();
						nRet = -1;
					}
#else
					nBlock = ::fcntl(m_clientSock, F_GETFL, 0);
					if (nBlock != -1)
					{
						nBlock |= O_NONBLOCK;
						nRet = ::fcntl(m_clientSock, F_SETFL, nBlock);
					}
#endif
					if ( -1 == nRet )
					{
						cout<<endl<<"Sock::TcpRecv socket error = "<<errno;
					}
				}
				continue;
			}
			else
			{
				tmpSock = m_clientSock;
			}
#else
		tmpSock = m_socket;
#endif

		if ( 0 == nNeeRecvLen ) //接收消息32位头
		{
			nNeeRecvLen = HEADER_SIZE;
			nRecvLen = recv(tmpSock, szbuf, nNeeRecvLen, 0);
#ifdef WIN32
			if ( SOCKET_ERROR == nRecvLen )
			{
				errno = WSAGetLastError();
				if ( EWOULDBLOCK == errno )
				{
#else
			if ( -1 == nRecvLen )
			{
				if ( EAGAIN == errno )
				{
					errno = EWOULDBLOCK;
#endif
				}
				else 
				{
					cout<<endl<<"Sock::TcpRecv socket error = "<<errno;
				}
			
			}
			else if ( nRecvLen == 0 ) //套接字关闭
			{
				continue;
			}
			else
			{
				int nExtLen = (unsigned int)szbuf[4];
				if (nExtLen > 0)
				{
					nNeeRecvLen = nExtLen;
					ntmpLen = nRecvLen;
				}
			}
		}
		else //接收消息扩展数据
		{
			nRecvLen = recv(tmpSock, szbuf+ntmpLen, nNeeRecvLen, 0);
#ifdef WIN32
			if ( SOCKET_ERROR == nRecvLen )
			{
				errno = WSAGetLastError();
				if ( EWOULDBLOCK == errno )
				{
#else
			if ( -1 == nRecvLen )
			{
				if ( EAGAIN == errno )
				{
					errno = EWOULDBLOCK;
#endif
				}
				else 
				{
					cout<<endl<<"Sock::TcpRecv socket error = "<<errno;
				}

			}
			else if ( nRecvLen == 0 ) //套接字关闭
			{
				continue;
			}
			else
			{
				if (nNeeRecvLen == nRecvLen)
				{
					szbuf[nRecvLen] = '\0';
					//处理数据
					DealData(szbuf, nRecvLen+ntmpLen);
					nNeeRecvLen = 0;
					ntmpLen = 0;
				}
			}
		}

		}

	}

	if (m_socket)
	{
		closesocket(m_socket);
		m_socket = INVALID_SOCKET;
	}
	if (m_clientSock)
	{
		closesocket(m_clientSock);
		m_clientSock = INVALID_SOCKET;
	}
	
	return 1;
}

int Sock::UdpRecv()
{
	int fds,nRet;
	timeval tv;
	char szbuf[1024*4];
	int iTotal;
	fd_set fd_recv;
	FD_ZERO(&fd_recv);
	sockaddr_in addr;
	int iAddrSize = sizeof(addr);

	while (true)
	{
		if (m_bExitRecv)
		{
			break;
		}

		tv.tv_sec = 0;
		tv.tv_usec = 100000;
		memset(szbuf,0,sizeof(szbuf));
		fds = 0;
		FD_ZERO(&fd_recv);
		FD_SET(m_socket,&fd_recv);
		fds = m_socket;

		if ( fds <=0 )
		{
#ifdef WIN32
			Sleep(1);
#else
			sleep(1);
#endif
			continue;
		}
		iTotal = select(fds + 1, &fd_recv, 0, 0, &tv);

#ifdef WIN32
		if ( SOCKET_ERROR == iTotal )
		{
			errno = WSAGetLastError();
			cout<<endl<<"Sock::UdpRecv socket error = "<<errno;
		}
#else
		if ( -1 == iTotal )
		{
			cout<<endl<<"Sock::UdpRecv socket error = "<<errno;
		}
#endif
		if ( iTotal == 0 ) //超时
		{
			continue;
		}

		if ( FD_ISSET(m_socket,&fd_recv) )//接收
		{
			bool bCanRecv = true;
			while ( bCanRecv )
			{
			
				iAddrSize = sizeof(addr);
				nRet = recvfrom(m_socket,szbuf,sizeof(szbuf),0,
					(struct sockaddr*)&addr,
#if !defined(WIN32)
					(socklen_t*)
#endif
					&iAddrSize);
#ifdef WIN32
				if ( SOCKET_ERROR == nRet )
				{
					errno = WSAGetLastError();
					if ( EWOULDBLOCK == errno )
					{
#else
				if ( -1 == nRet )
				{
					if ( EAGAIN == errno )
					{
						errno = EWOULDBLOCK;
#endif
					}
					else //error
					{
						cout<<endl<<"Sock::UdpRecv socket error = "<<errno;
					}
					
					bCanRecv = false;
				}
				else
				{
					//处理数据
					DealData(szbuf, nRet);
				}
			}
		}
	}

	if (m_socket)
	{
		closesocket(m_socket);
		m_socket = INVALID_SOCKET;
	}

	return 1;
}


int Sock::DealData(char *pData, int nLength)
{
	int nExtLen = 0;
	char szBuf[1024];
	memset(szBuf, 0, sizeof(szBuf));
	if (0xA0 == (unsigned char)pData[1])//服务端收到来自客户端的登录请求
	{
		nExtLen = (unsigned int)pData[4];
		memcpy(szBuf, pData+HEADER_SIZE, nExtLen);
		cout<<endl<<"receive data : "<<szBuf;

		memset(szBuf,0,sizeof(szBuf));
		szBuf[0] = (char)0xCC;
		szBuf[1] = (char)0xA1; //登录请求应答
		char *p = "Hello client!";
		nExtLen = strlen(p);
		memcpy(szBuf + 4, &nExtLen, sizeof(unsigned int));
		strcpy(szBuf + HEADER_SIZE, p);
		SendData(szBuf, HEADER_SIZE + nExtLen);
	}
	else if (0xA1 == (unsigned char)pData[1])//客户端收到来自服务端的登录请求应答
	{
		nExtLen = (unsigned int)pData[4];
		memcpy(szBuf, pData+HEADER_SIZE, nExtLen);
		cout<<endl<<"receive data : "<<szBuf;

		memset(szBuf,0,sizeof(szBuf));
		szBuf[0] = (char)0xCC; //发送退出请求
		szBuf[1] = (char)0xB0;
		char *p = "Hello Server, exit now";
		nExtLen = strlen(p);
		memcpy(szBuf + 4, &nExtLen, sizeof(unsigned int));
		strcpy(szBuf + HEADER_SIZE, p);
		SendData(szBuf, HEADER_SIZE + nExtLen);

		m_bExitRecv = true;//本端退出
	}
	else if (0xB0 == (unsigned char)pData[1]) //服务端收到来自客户端的退出请求
	{
		m_bExitRecv = true;//服务端退出
	}

	return 1;
}


下边是Win32和Linux上的截图

用select模式收发处理数据(跨平台)_第1张图片

 

 

用select模式收发处理数据(跨平台)_第2张图片

 

用select模式收发处理数据(跨平台)_第3张图片

 

  

你可能感兴趣的:(struct,tcp,socket,server,header,跨平台)