本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上的截图