對於任何一個支援網路通訊的應用程式,它都有其自身的應用程式層通訊協定。該協議規定了應用程式發送什麼樣格式的網路資料或接收到某某資料時回送的資料格式。該協議是由應用程式層相關人員定製的。
不同的應用程式有各自不同的網路資料處理模組,這似乎是不可移植的,但網路資料的發送和接收則是獨立於具體的應用程式層協議的。網路資料的發送和接收模組只與底層平台(作業系統)相關,而與應用程式層協議無關,它們只負責資料收發的穩定性、可靠性,而不管資料的具體含義。這一部分是可以移植的,我們可以編寫強大的資料收發類以使其滿足任何網路應用程式對於收發資料的需求。這樣,當我們下次再編寫應用程式的通訊模組時則只需要關注資料如何解析,而不用處理資料的收發。
宏定義檔案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.og++ -o main main.o client_logic.o tcp_client.o server_logic.o tcp_server.o -lpthreadmain.o:main.cppg++ -c main.cpp -lpthreadclient_logic.o:client_logic.cpp g++ -c client_logic.cpp -lpthreadtcp_client.o:tcp_client.cppg++ -c tcp_client.cpp -lpthreadserver_logic.o:server_logic.cppg++ -c server_logic.cpp -lpthreadtcp_server.o:tcp_server.cppg++ -c tcp_server.cpp -lpthreadclean:rm *.o main