对于任何一个支持网络通信的应用程序,它都有其自身的应用层通信协议。该协议规定了应用程序发送什么样格式的网络数据或接收到某某数据时回送的数据格式。该协议是由应用层相关人员定制的。
不同的应用程序有各自不同的网络数据处理模块,这似乎是不可移植的,但网络数据的发送和接收则是独立于具体的应用层协议的。网络数据的发送和接收模块只与底层平台(操作系统)相关,而与应用层协议无关,它们只负责数据收发的稳定性、可靠性,而不管数据的具体含义。这一部分是可以移植的,我们可以编写强大的数据收发类以使其满足任何网络应用程序对于收发数据的需求。这样,当我们下次再编写应用程序的通信模块时则只需要关注数据如何解析,而不用处理数据的收发。
宏定义文件def.h:
#ifndef __DEF_H__
#define __DEF_H__
#include <stdio.h>
#define BUF_LEN 100
#define TIMEVAL_SEND 6
#define TIMEVAL_RECV 3
#define TIMEVAL_ACCEPT 3
#define TIMEVAL_RECONNECT 5
#define TIMEVAL_THREAD 100000000
#ifndef BOOL
#define BOOL int
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define mylog printf
#endif
客户端数据收发类定义文件(tcp_client.h):
#ifndef __TCP_CLIENT_H__
#define __TCP_CLIENT_H__
#include "def.h"
class CClientLogic;
class CTCPClient
{
public:
CTCPClient(const char* ip, int port);
~CTCPClient();
int init();
int uninit();
int setLogicObject(CClientLogic* pClientLogic);
int connectServer();
int sendData(const char* buf, int len);
int recvData(char* buf, int len);
private:
int offline();
int online();
private:
char m_ip[BUF_LEN];
int m_port;
int m_sockfd;
CClientLogic* m_logic;
};
#endif
客户端数据收发类实现文件(tcp_client.cpp):
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/tcp.h>
#include <errno.h>
#include "client_logic.h"
#include "tcp_client.h"
CTCPClient::CTCPClient(const char* ip, int port)
{
strncpy(m_ip, ip, BUF_LEN);
m_port = port;
m_sockfd = -1;
}
CTCPClient::~CTCPClient()
{
uninit();
}
int CTCPClient::init()
{
return 0;
}
int CTCPClient::uninit()
{
if (m_sockfd > 0)
{
close(m_sockfd);
m_sockfd = -1;
}
return 0;
}
int CTCPClient::setLogicObject(CClientLogic* pClientLogic)
{
if (!pClientLogic)
{
mylog("tcpclient: client object is null.\n");
return -1;
}
m_logic = pClientLogic;
return 0;
}
int CTCPClient::connectServer()
{
if (m_sockfd > 0)
{
offline();
}
if ((m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
mylog("tcpclient: create socket failed.\n");
return -1;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(m_ip);
servaddr.sin_port = htons(m_port);
if (connect(m_sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
close(m_sockfd);
m_sockfd = -1;
mylog("tcpclient: can not connect to server.\n");
return -1;
}
/*
* set socket options
*/
int iKeepIdle = 60;
int iKeepInterval = 60;
int iKeepCount = 3;
int iOne = 1;
setsockopt(m_sockfd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&iOne, sizeof(int));
setsockopt(m_sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (const void *)&iKeepIdle, sizeof(int));
setsockopt(m_sockfd, IPPROTO_TCP, TCP_KEEPINTVL, (const void *)&iKeepInterval, sizeof(int));
setsockopt(m_sockfd, IPPROTO_TCP, TCP_KEEPCNT, (const void *)&iKeepCount, sizeof(int));
online();
return 0;
}
int CTCPClient::sendData(const char* buf, int len)
{
if (NULL == buf || len <= 0)
{
mylog("tcpclient: data(send) illegal.\n");
return -1;
}
if (m_sockfd < 0)
{
mylog("tcpclient: connecttion has not been established, send data faield.\n");
return -1;
}
fd_set set_w;
struct timeval timeout;
FD_ZERO(&set_w);
FD_SET(m_sockfd, &set_w);
timeout.tv_sec = TIMEVAL_SEND;
timeout.tv_usec = 0;
if (select(m_sockfd + 1, NULL, &set_w, NULL, &timeout) <= 0)
{
mylog("tcpclient: network status is busy, failed to send data.\n");
offline();
return -1;
}
int sendLen = 0;
if (FD_ISSET(m_sockfd, &set_w))
{
sendLen = send(m_sockfd, buf, len, 0);
if (sendLen < len)
{
if (sendLen >= 0)
mylog("tcpclient: send data less than request.\n");
else
{
if (EMSGSIZE == errno)
{
mylog("tcpclient: buffer to be sent is too large.\n");
}
else if (EAGAIN != errno && EWOULDBLOCK != errno)
{
offline();
return -1;
}
}
mylog("tcpclient: send data failed.\n");
return -1;
}
}
return sendLen;
}
int CTCPClient::recvData(char* buf, int len)
{
if (m_sockfd < 0)
{
mylog("tcpclient: connection has not been established.recv data failed.\n");
return -1;
}
fd_set set_r;
struct timeval timeout;
FD_ZERO(&set_r);
FD_SET(m_sockfd, &set_r);
timeout.tv_sec = TIMEVAL_RECV;
timeout.tv_usec = 0;
int ret = select(m_sockfd + 1, &set_r, NULL, NULL, &timeout);
if (ret < 0)
{
mylog("tcpclient: select failed, recv data failed.\n");
offline();
return -1;
}
else if (0 == ret)
{
return -1;
}
if (FD_ISSET(m_sockfd, &set_r))
{
int recvLen = recv(m_sockfd, buf, len, 0);
if (recvLen <= 0)
{
if (ETIMEDOUT != errno && EWOULDBLOCK != errno)
{
mylog("tcpclient: recv data failed.\n");
offline();
}
return -1;
}
return recvLen;
}
return -1;
}
int CTCPClient::offline()
{
if (m_sockfd > 0)
{
close(m_sockfd);
m_sockfd = -1;
}
if (m_logic)
m_logic->offline();
return 0;
}
int CTCPClient::online()
{
if (m_logic)
m_logic->online();
}
服务器端数据收发类定义文件(tcp_server.h):
#ifndef __TCP_SERVER_H__
#define __TCP_SERVER_H__
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "def.h"
class CServerLogic;
class CTCPServer
{
public:
CTCPServer(int port);
~CTCPServer();
int init();
int uninit();
int setLogicObject(CServerLogic* pServerLogic);
int acceptClient();
int sendData(const char* buf, int len);
int recvData(char* buf, int len);
int getStrAddr(char* ip, int len);
private:
int offline();
int online();
private:
int m_port;
int m_sockfd;
int m_clifd;
struct sockaddr_in m_cliaddr;
CServerLogic* m_logic;
};
#endif
服务器端数据收发类实现文件(tcp_server.cpp):
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include "server_logic.h"
#include "tcp_server.h"
CTCPServer::CTCPServer(int port)
{
m_port = port;
m_sockfd = -1;
m_clifd = -1;
memset(&m_cliaddr, 0, sizeof(m_cliaddr));
}
CTCPServer::~CTCPServer()
{
uninit();
}
int CTCPServer::init()
{
if ((m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
mylog("tcpserver: create socket failed.\n");
return -1;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(m_port);
if (bind(m_sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
mylog("tcpserver: bind error.\n");
close(m_sockfd);
m_sockfd = -1;
return -1;
}
if (listen(m_sockfd, 9) < 0)
{
mylog("tcpserver: listen error.\n");
close(m_sockfd);
m_sockfd = -1;
return -1;
}
return 0;
}
int CTCPServer::uninit()
{
if (m_clifd > 0)
{
close(m_clifd);
m_clifd = -1;
}
if (m_sockfd > 0)
{
close(m_sockfd);
m_sockfd = -1;
}
return 0;
}
int CTCPServer::setLogicObject(CServerLogic* pServerLogic)
{
if (!pServerLogic)
{
mylog("tcpserver: server logic object is null.\n");
return -1;
}
m_logic = pServerLogic;
return 0;
}
int CTCPServer::acceptClient()
{
if (m_sockfd < 0)
{
mylog("tcpserver: server socket not created, accept client failed.\n");
return -1;
}
if (m_clifd > 0)
{
offline();// disconnect
}
fd_set set_r;
struct timeval timeout;
FD_ZERO(&set_r);
FD_SET(m_sockfd, &set_r);
timeout.tv_sec = TIMEVAL_ACCEPT;
timeout.tv_usec = 0;
if (select(m_sockfd + 1, &set_r, NULL, NULL, &timeout) <= 0)
return -1;
if (FD_ISSET(m_sockfd, &set_r))// if socket is ready to accept
{
socklen_t clilen = sizeof(m_cliaddr);
if ((m_clifd = accept(m_sockfd, (struct sockaddr*)&m_cliaddr, &clilen)) < 0)
{
mylog("tcpserver: accept error.\n");
return -1;
}
online();// connection is build
return 0;
}
return -1;
}
int CTCPServer::sendData(const char* buf, int len)
{
if (NULL == buf || len <= 0)
{
mylog("tcpserver: data(send) illegal.\n");
return -1;
}
if (m_clifd < 0)
{
mylog("tcpserver: connecttion has not been established,send data faield.\n");
return -1;
}
fd_set set_w;
struct timeval timeout;
FD_ZERO(&set_w);
FD_SET(m_clifd, &set_w);
timeout.tv_sec = TIMEVAL_SEND;
timeout.tv_usec = 0;
if (select(m_clifd + 1, NULL, &set_w, NULL, &timeout) <= 0)
{
mylog("tcpserver: network status is busy, failed to send data.\n");
offline();
return -1;
}
int sendLen = 0;
if (FD_ISSET(m_clifd, &set_w))
{
sendLen = send(m_sockfd, buf, len, 0);
if (sendLen < len)
{
if (sendLen >= 0)
mylog("tcpserver: send data less than request.\n");
else
{
if (EMSGSIZE == errno)
{
mylog("tcpserver: buffer to be sent is too large.\n");
}
else if (EAGAIN != errno && EWOULDBLOCK != errno)
{
offline();
return -1;
}
}
mylog("tcpserver: send data failed.\n");
return -1;
}
}
return sendLen;
}
int CTCPServer::recvData(char* buf, int len)
{
if (m_clifd < 0)
{
mylog("tcpserver: connection has not been established, recv data failed.\n");
return -1;
}
fd_set set_r;
struct timeval timeout;
FD_ZERO(&set_r);
FD_SET(m_clifd, &set_r);
timeout.tv_sec = TIMEVAL_RECV;
timeout.tv_usec = 0;
int ret = select(m_clifd + 1, &set_r, NULL, NULL, &timeout);
if (ret < 0)
{
mylog("tcpserver: select failed, recv data failed.\n");
offline();
return -1;
}
else if (0 == ret)// timeout
{
return -1;
}
if (FD_ISSET(m_clifd, &set_r))
{
int recvLen = recv(m_clifd, buf, len, 0);
if (recvLen <= 0)
{
if (ETIMEDOUT != errno && EWOULDBLOCK != errno)
{
mylog("tcpserver: recv data failed.\n");
offline();
}
return -1;
}
return recvLen;
}
return -1;
}
int CTCPServer::offline()
{
if (m_clifd > 0)
{
close(m_clifd);
m_clifd = -1;
}
if (m_logic)
m_logic->offline();
return 0;
}
int CTCPServer::online()
{
if (m_logic)
m_logic->online();
}
int CTCPServer::getStrAddr(char* ip, int len)
{
if (m_clifd < 0)// connection hasn't been established
return -1;
strncpy(ip, inet_ntoa(m_cliaddr.sin_addr), len);
return 0;
}
即时通讯应用程序客户端逻辑处理类定义(client_logic.h):
#ifndef __CLIENT_LOGIC_H__
#define __CLIENT_LOGiC_H__
#include <pthread.h>
#include <semaphore.h>
#include <iostream>
#include <queue>
#include <cstring>
#include <string>
#include "tcp_client.h"
class CClientLogic:public CTCPClient
{
public:
CClientLogic(const char* ip, int port);
~CClientLogic();
int init();
int uninit();
int start();
int stop();
int online();
int offline();
std::queue<std::string>& getDataQue();
private:
static void* clientThread(void* args);
private:
pthread_t m_tid;
pthread_attr_t m_attr;
sem_t m_sem;
BOOL m_runFlag;
BOOL m_isConnected;
std::queue<std::string> m_dataQue;
};
#endif
即时通讯应用程序客户端逻辑处理类实现(client_logic.cpp):
#include <pthread.h>
#include <semaphore.h>
#include "client_logic.h"
CClientLogic::CClientLogic(const char* ip, int port):CTCPClient(ip, port)
{
m_runFlag = FALSE;
m_isConnected = FALSE;
}
CClientLogic::~CClientLogic()
{
}
int CClientLogic::init()
{
if (CTCPClient::init() != 0)
return -1;
sem_init(&m_sem, 0, 0);
pthread_attr_init(&m_attr);
setLogicObject(this);
return 0;
}
int CClientLogic::uninit()
{
pthread_attr_destroy(&m_attr);
sem_destroy(&m_sem);
if (CTCPClient::uninit() != 0)
return -1;
return 0;
}
int CClientLogic::start()
{
if (m_runFlag)
{
mylog("client: thread already started.\n");
return 0;
}
m_runFlag = TRUE;
if(pthread_create(&m_tid, &m_attr, clientThread, this) == 0)
{
return 0;
}
else
{
mylog("client: thread created failed.\n");
m_runFlag = FALSE;
return -1;
}
}
int CClientLogic::stop()
{
if (!m_runFlag)
{
mylog("client: thread was not started.\n");
return 0;
}
m_runFlag = FALSE;
sem_post(&m_sem);
pthread_join(m_tid, NULL);
return 0;
}
void* CClientLogic::clientThread(void* args)
{
CClientLogic* thiz = (CClientLogic*)args;
struct timespec ts;
while (thiz->m_runFlag)
{
if (!thiz->m_dataQue.empty())
{
if (thiz->m_isConnected)// connection is established
{
std::string str = thiz->m_dataQue.front();
thiz->m_dataQue.pop();
thiz->sendData(str.c_str(), str.length());
}
else
{
if (thiz->connectServer() != 0)
{
ts.tv_sec = time(NULL) + TIMEVAL_RECONNECT;
ts.tv_nsec = 0;
sem_timedwait(&thiz->m_sem, &ts);
continue;
}
}
}
ts.tv_sec = time(NULL);
ts.tv_nsec = TIMEVAL_THREAD;
sem_timedwait(&thiz->m_sem, &ts);
}
}
std::queue<std::string>& CClientLogic::getDataQue()
{
return m_dataQue;
}
int CClientLogic::online()
{
m_isConnected = TRUE;
return 0;
}
int CClientLogic::offline()
{
m_isConnected = FALSE;
return 0;
}
即时通讯应用程序服务器端逻辑处理类定义(server_logic.h):
#ifndef __SERVER_LOGIC_H__
#define __SERVER_LOGIC_H__
#include <pthread.h>
#include <semaphore.h>
#include "tcp_server.h"
class CServerLogic:public CTCPServer
{
public:
CServerLogic(int port);
~CServerLogic();
int init();
int uninit();
int start();
int stop();
int online();
int offline();
private:
static void* serverThread(void* args);
private:
pthread_t m_tid;
pthread_attr_t m_attr;
sem_t m_sem;
BOOL m_runFlag;
BOOL m_isConnected;
};
#endif
即时通讯应用程序服务器端逻辑处理类实现(server_logic.cpp):
#include <pthread.h>
#include <semaphore.h>
#include "server_logic.h"
CServerLogic::CServerLogic(int port):CTCPServer(port)
{
m_runFlag = FALSE;
m_isConnected = FALSE;
}
CServerLogic::~CServerLogic()
{}
int CServerLogic::init()
{
if (CTCPServer::init() != 0)
return -1;
sem_init(&m_sem, 0, 0);
pthread_attr_init(&m_attr);
setLogicObject(this);
return 0;
}
int CServerLogic::uninit()
{
pthread_attr_destroy(&m_attr);
sem_destroy(&m_sem);
if (CTCPServer::uninit() != 0)
return -1;
return 0;
}
int CServerLogic::online()
{
m_isConnected = TRUE;
return 0;
}
int CServerLogic::offline()
{
m_isConnected = FALSE;
return 0;
}
int CServerLogic::start()
{
if (m_runFlag)
{
mylog("server: thread already started.\n");
return 0;
}
m_runFlag = TRUE;
if (pthread_create(&m_tid, &m_attr, serverThread, this) == 0)
{
return 0;
}
else
{
m_runFlag = FALSE;
return -1;
}
}
int CServerLogic::stop()
{
if (!m_runFlag)
{
mylog("server: thread is not running.\n");
return 0;
}
m_runFlag = FALSE;
sem_post(&m_sem);
pthread_join(m_tid, NULL);
return 0;
}
void* CServerLogic::serverThread(void* args)
{
CServerLogic* thiz = (CServerLogic*)args;
struct timespec ts;
char recvbuf[BUF_LEN];
char ipBuf[BUF_LEN] = {0};
while (thiz->m_runFlag)
{
if (thiz->m_isConnected)// connection already established
{
int recvLen = thiz->recvData(recvbuf, BUF_LEN - 1);
if (recvLen > 0)
{
recvbuf[recvLen] = '\0';
thiz->getStrAddr(ipBuf, BUF_LEN);
printf("%s: %s\n", ipBuf, recvbuf);
}
}
else
{
thiz->acceptClient();
}
ts.tv_sec = time(NULL);
ts.tv_nsec = TIMEVAL_THREAD;
sem_timedwait(&thiz->m_sem, &ts);
}
}
main.cpp文件:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <queue>
#include "server_logic.h"
#include "client_logic.h"
int main(int argc, char* argv[])
{
char ip[BUF_LEN];
if (argc != 2)
strncpy(ip, "127.0.0.1", BUF_LEN);
else
strncpy(ip, argv[1], BUF_LEN);
CServerLogic servLogic(8889);
if (servLogic.init() != 0)
{
printf("server init failed.\n");
return 0;
}
servLogic.start();
CClientLogic cliLogic(ip, 8889);
cliLogic.init();
cliLogic.start();
std::queue<std::string>& que = cliLogic.getDataQue();
char sendbuf[BUF_LEN];
while (gets(sendbuf) && strncmp(sendbuf, "end", strlen("end")) != 0 )
{
que.push(sendbuf);
}
cliLogic.stop();
cliLogic.uninit();
servLogic.stop();
servLogic.uninit();
return 0;
}
makefile文件:
main:main.o client_logic.o tcp_client.o server_logic.o tcp_server.o
g++ -o main main.o client_logic.o tcp_client.o server_logic.o tcp_server.o -lpthread
main.o:main.cpp
g++ -c main.cpp -lpthread
client_logic.o:client_logic.cpp
g++ -c client_logic.cpp -lpthread
tcp_client.o:tcp_client.cpp
g++ -c tcp_client.cpp -lpthread
server_logic.o:server_logic.cpp
g++ -c server_logic.cpp -lpthread
tcp_server.o:tcp_server.cpp
g++ -c tcp_server.cpp -lpthread
clean:
rm *.o main