diy数据库(二)--网络通信类
来源:互联网 发布:vr软件app排行榜 编辑:程序博客网 时间:2024/05/17 03:19
一、首先,我们先实现OSS层的ossSocket类,供数据库客户端和数据库引擎进行通信
友情提示:对应上面的类图的头文件和源代码附在了本文的最下面。
int _fd ;//socket的文件描述符
socklen_t _addressLen ;//地址长度
socklen_t _peerAddressLen ;//对方地址的长度
struct sockaddr_in _sockAddress ;//本地的socket地址 ipv4
struct sockaddr_in _peerAddress ;//对方的socket地址 ipv4
bool _init ;//代表是否已经初始化
int _timeout ;//代表超时时间,包括读和写的超时
这是一个基础类,都是对socket的通用的操作,实现方法也比较常见。这里主要注意的有几点:
1.读写sockt时要注意信号中断带来的错误ENTRY,对于中断,我们要手动处理。
2.由于数据库需的操作需要有实时性,我们需要设置TCP_NODELAY套接字选项,来关闭TCP的Nagle算法
3.我们的ossSocket类中的读写函数有一个特点就是增加了时间限制,即我们要求在给定时间参数的范围类得带可读或可写的socket,不然判断读写超时
4.为了防止socket进入timewait状态,我们需要设置SO_REUSEADDR套接字选项
5.当向一个已经收到RST的套接字执行写操作时,内核会向用户进程发送一个SIGPIPE信号。该信号的默认行为是终止进程。send()函数的最后一个参数可以设MSG_NOSIGNAL,来禁止内核发送SIGPIPE信号。
附录:
以下是头文件:ossSocket.hpp
#ifndef OSSNETWORK_HPP_#define OSSNETWORK_HPP_#include "core.hpp"#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <netdb.h>#include <netinet/tcp.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#define SOCKET_GETLASTERROR errno//系统的错误码#define OSS_EAGAINEAGAIN#define OSS_EINTREINTR#define closesocketclose// by default 10ms timeout#define OSS_SOCKET_DFT_TIMEOUT 10000//默认10毫秒的超时// max hostname#define OSS_MAX_HOSTNAME NI_MAXHOST//hostname的#define OSS_MAX_SERVICENAME NI_MAXSERV#include "core.hpp"typedef intSOCKET;class _ossSocket{private ://所有 int _fd ;//socket的文件描述符 socklen_t _addressLen ;//地址长度 socklen_t _peerAddressLen ;//对方地址的长度 struct sockaddr_in _sockAddress ;//本地的socket地址 ipv4 struct sockaddr_in _peerAddress ;//对方的socket地址 ipv4 bool _init ;//代表是否已经初始化 int _timeout ;//代表超时时间,包括读和写的超时protected: unsigned int _getPort ( sockaddr_in *addr ) ;//得到端口 int _getAddress ( sockaddr_in *addr, char *pAddress, unsigned int length ) ;//得到地址public : int setSocketLi ( int lOnOff, int linger ) ;//设置close连接时对发送缓冲区中数据的操作 void setAddress(const char * pHostName, unsigned int port ); // Create a listening socket _ossSocket();//设置默认的socket的属性 _ossSocket ( unsigned int port, int timeout = 0 ) ;//设置socket的属性 _ossSocket ( const char *pHostname, unsigned int port, int timeout = 0 ) ;<span style="font-family: Arial, Helvetica, sans-serif;">// 创建一个客户端socket</span> _ossSocket ( int *sock, int timeout = 0 ) ;<span style="font-family: Arial, Helvetica, sans-serif;">//通过accept的到的socket的fd来构建</span> ~_ossSocket () { close () ; } int initSocket () ;//初始化函数 int bind_listen () ;//将socket绑定到一个端口 bool isConnected () ;//判断连接状态 int send ( const char *pMsg, int len, int timeout = OSS_SOCKET_DFT_TIMEOUT, int flags = 0 ) ; int recv ( char *pMsg, int len, int timeout = OSS_SOCKET_DFT_TIMEOUT, int flags = 0 ) ;//收到固定长度的数据才返回(timeout=0表示永远不会超时) int recvNF ( char *pMsg, int &len, int timeout = OSS_SOCKET_DFT_TIMEOUT ) ;//不用收到固定长度的数据 int connect () ; void close () ; int accept ( int *sock, struct sockaddr *addr, socklen_t *addrlen, int timeout = OSS_SOCKET_DFT_TIMEOUT ) ; //一下是几个辅助函数 /*当我们发送小包的时候,tcp会把几个小包组合成一个大包来发送,这个功能可以关闭*/ int disableNagle () ;//关闭打包发送特性,关闭tcp中的Nagle功能,小包的发送会更实时 unsigned int getPeerPort () ; int getPeerAddress ( char *pAddress, unsigned int length ) ; unsigned int getLocalPort () ; int getLocalAddress ( char *pAddress, unsigned int length ) ; int setTimeout ( int seconds ) ;//设置超时 static int getHostName ( char *pName, int nameLen ) ;//得到域名 static int getPort ( const char *pServiceName, unsigned short &port ) ;//把服务名转化为端口号,所有的服务都在/etc/server文件里面 int setAnsyn () ;};typedef class _ossSocket ossSocket ;#endif
以下是实现ossSockt.cpp
#include "ossSocket.hpp"#include "pd.hpp"//创建一个监听的socket_ossSocket::_ossSocket ( unsigned int port, int timeout ){//设置监听套接字的各种属性 _init = false ; _fd = 0 ; _timeout = timeout ; memset ( &_sockAddress, 0, sizeof(sockaddr_in) ) ; memset ( &_peerAddress, 0, sizeof(sockaddr_in) ) ; _peerAddressLen = sizeof(_peerAddress) ; _addressLen = sizeof( _sockAddress ) ; //监听套接字特有 _sockAddress.sin_family = AF_INET ; _sockAddress.sin_addr.s_addr = htonl ( INADDR_ANY ) ; _sockAddress.sin_port = htons ( port ) ; }// Create a socket_ossSocket::_ossSocket (){ _init = false ; _fd = 0 ; _timeout = 0 ; memset ( &_sockAddress, 0, sizeof(sockaddr_in) ) ; memset ( &_peerAddress, 0, sizeof(sockaddr_in) ) ; _peerAddressLen = sizeof(_peerAddress) ; _addressLen = sizeof( _sockAddress ) ;}// 创建一个客户端的套接字//pHostname是对方机器的机器名(如果不是机器名就当做对方机器的ip地址),port是对方机器的端口号_ossSocket::_ossSocket ( const char *pHostname, unsigned int port, int timeout ){ struct hostent *hp ; _init = false ; _timeout = timeout ; _fd = 0 ; memset ( &_sockAddress, 0, sizeof(sockaddr_in) ) ; memset ( &_peerAddress, 0, sizeof(sockaddr_in) ) ; _peerAddressLen = sizeof(_peerAddress) ; _sockAddress.sin_family = AF_INET ; if ( (hp = gethostbyname ( pHostname ) ) )// _sockAddress.sin_addr.s_addr = *((int*)hp->h_addr_list[0] ) ; else _sockAddress.sin_addr.s_addr = inet_addr ( pHostname ) ; _sockAddress.sin_port = htons ( port ) ; _addressLen = sizeof(_sockAddress ) ;}// 由已经得到的套接字来创建socket_ossSocket::_ossSocket ( int *sock, int timeout )//SOCKET就是int类型{ int rc = DIY_OK ; _fd = *sock ; _init = true ; _timeout = timeout ; _addressLen = sizeof( _sockAddress ) ; memset ( &_peerAddress, 0, sizeof(sockaddr_in) ) ; _peerAddressLen = sizeof(_peerAddress) ; rc = getsockname ( _fd, (sockaddr*)&_sockAddress, &_addressLen ) ;//得到本地的ip地址 if ( rc ) { //PD_LOG ( PDERROR, "Failed to get sock name, error = %d", // SOCKET_GETLASTERROR ) ; _init = false ; } else { rc = getpeername ( _fd, (sockaddr*)&_peerAddress, &_peerAddressLen ) ;//得到远端的ip地址 //PD_RC_CHECK ( rc, PDERROR, "Failed to get peer name, error = %d", //SOCKET_GETLASTERROR ) ; }done : return ;error : goto done ;}int _ossSocket::initSocket (){ int rc = DIY_OK ; if ( _init ) { goto done ; } memset ( &_peerAddress, 0, sizeof(sockaddr_in) ) ; _peerAddressLen = sizeof(_peerAddress) ; _fd =socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ;//创建socket if ( -1 == _fd ) { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to initialize socket, error = %d", // SOCKET_GETLASTERROR ) ; goto error; } _init =true ; // set timeout setTimeout ( _timeout ) ;done : return rc ;error : goto done ;}//设置SO_LINGER选项int _ossSocket::setSocketLi ( int lOnOff, int linger ){ int rc = DIY_OK ; struct linger _linger ; _linger.l_onoff = lOnOff ; _linger.l_linger = linger ; rc = setsockopt ( _fd, SOL_SOCKET, SO_LINGER, (const char*)&_linger, sizeof(_linger) ) ; return rc ;}void _ossSocket::setAddress(const char *pHostname, unsigned int port ){ struct hostent *hp ; memset ( &_sockAddress, 0, sizeof(sockaddr_in ) ) ; memset ( &_peerAddress, 0, sizeof(sockaddr_in) ) ; _peerAddressLen = sizeof(_peerAddress) ; _sockAddress.sin_family = AF_INET ; if ( ( hp = gethostbyname ( pHostname ))) _sockAddress.sin_addr.s_addr = *((int*)hp->h_addr_list[0] ) ; else _sockAddress.sin_addr.s_addr = inet_addr ( pHostname ) ; _sockAddress.sin_port = htons ( port ) ; _addressLen = sizeof(_sockAddress ) ;}//监听端口int _ossSocket::bind_listen (){ int rc = DIY_OK ; int temp = 1 ; rc = setsockopt ( _fd, SOL_SOCKET, SO_REUSEADDR, (char*)&temp, sizeof(int) ) ;//SO_REUSEADDR确定这个端口可以重复使用 if ( rc ) { //PD_LOG ( PDWARNING, "Failed to setsockopt SO_REUSEADDR, rc = %d", // SOCKET_GETLASTERROR ) ; goto error; } rc = setSocketLi ( 1, 30 ) ; if ( rc ) { //PD_LOG ( PDWARNING, "Failed to setsockopt SO_LINGER, rc = %d", // SOCKET_GETLASTERROR ) ; goto error; } rc = ::bind ( _fd, (struct sockaddr*)&_sockAddress, _addressLen ) ; if ( rc ) { //PD_RC_CHECK( DIY_NETWORK, PDERROR, // "Failed to bind socket, rc = %d", SOCKET_GETLASTERROR ) ; goto error; } rc = listen ( _fd, SOMAXCONN ) ; if ( rc ) { //PD_RC_CHECK( DIY_NETWORK, PDERROR, // "Failed to listen socket, rc = %d", SOCKET_GETLASTERROR ) ; goto error; }done : return rc ;error : close () ; goto done ;}//发送函数//发送函数也有超时timeoutint _ossSocket::send ( const char *pMsg, int len, int timeout, int flags ){ int rc = DIY_OK ; intmaxFD = _fd ; struct timeval maxSelectTime ; fd_set fds ; maxSelectTime.tv_sec = timeout / 1000000 ; maxSelectTime.tv_usec = timeout % 1000000 ; // if len == 0, then let's just return if ( 0 == len ) { return DIY_OK ; } //监测在时间范围类socket是否可写 while ( true ) { FD_ZERO ( &fds ) ; FD_SET ( _fd, &fds ) ; rc = select ( (int)(maxFD + 1), NULL, &fds, NULL, timeout>=0?&maxSelectTime:NULL ) ; if ( 0 == rc )//rc=0表示超时 { // timeout rc = DIY_TIMEOUT ;//超时 goto done ; } // if < 0, something wrong if ( 0 > rc )//rc<0表示有错误 { rc = error ; if ( EINTR == rc )//如果失败是因为收到中断,继续 { continue ; } //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to select from socket, rc = %d", rc ) ; } if ( FD_ISSET ( _fd, &fds ) ) { break ; } } while ( len > 0 ) { rc = ::send ( _fd, pMsg, len, MSG_NOSIGNAL | flags ) ;//禁止send()函数向系统发送异常消息,不收到sigpipe if ( rc <= 0 ) {if(rc<0&&errno==EINTR)rc=0;//如果失败是因为中断,继续else { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to send, rc = %d", SOCKET_GETLASTERROR ) ; rc=-1; goto error; } } len -= rc ; pMsg += rc ; } rc = DIY_OK ;done : return rc ;error : goto done ;}bool _ossSocket::isConnected (){ int rc = DIY_OK ; rc = ::send ( _fd, "", 0, MSG_NOSIGNAL ) ;//尝试发送一个零字节的信息 if ( 0 > rc ) return false ; return true ;}int _ossSocket::recv ( char *pMsg, int len, int timeout, int flags ){ int rc = DIY_OK ; int retries = 0 ; int maxFD = (int)_fd ; struct timeval maxSelectTime ; fd_set fds ; if ( 0 ==len ) return DIY_OK ; maxSelectTime.tv_sec = timeout / 1000000 ; maxSelectTime.tv_usec = timeout % 1000000 ; while ( true ) { FD_ZERO ( &fds ) ; FD_SET ( _fd, &fds ) ; rc = select ( maxFD+1, &fds, NULL, NULL, timeout>=0?&maxSelectTime:NULL ) ; // 0 means timeout if ( 0 == rc ) { rc = DIY_TIMEOUT ; goto done ; } // if < 0 something wrong if ( 0 > rc ) { rc = error ; if ( EINTR==rc ) { continue ; } //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to select from socket, rc = %d", rc ) ; rc=DIY_NETWORK; goto error; } if ( FD_ISSET ( _fd, &fds ) ) { break ; } } while ( len > 0 ) { rc = ::recv ( _fd, pMsg, len, MSG_NOSIGNAL|flags ) ; if ( rc > 0 ) { if ( flags & MSG_PEEK ) { goto done ; } len -= rc ; pMsg += rc ; } else if ( rc == 0 )//网络关闭,收到fin { //PD_RC_CHECK ( DIY_NETWORK_CLOSE, PDWARNING, // "Peer unexpected shutdown" ) ; goto error; } else { rc = error ; if ( EINTR == rc ) { continue ; } //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Recv() Failed: rc = %d", rc ) ; goto error; } } rc = DIY_OK ;done : return rc ;error : goto done ;}int _ossSocket::recvNF ( char *pMsg, int &len, int timeout ){ int rc = DIY_OK ; int retries = 0 ; int maxFD = (int)_fd ; struct timeval maxSelectTime ; fd_set fds ; // if we don't expect to receive anything, no need to continue if ( 0 == len ) return DIY_OK ; maxSelectTime.tv_sec = timeout / 1000000 ; maxSelectTime.tv_usec = timeout % 1000000 ; // wait loop until either we timeout or get a message while ( true ) { FD_ZERO ( &fds ) ; FD_SET ( _fd, &fds ) ; rc = select ( maxFD + 1, &fds, NULL, NULL, timeout>=0?&maxSelectTime:NULL ) ; // 0 means timeout if ( 0 == rc ) { rc = DIY_TIMEOUT ; goto done ; } // if < 0, means something wrong if ( 0 > rc ) { rc = error ; // if we failed due to interrupt, let's continue if ( EINTR == rc ) { continue ; } //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to select from socket, rc = %d", rc ) ; goto error; } if ( FD_ISSET ( _fd, &fds ) ) { break ; } } rc = ::recv ( _fd, pMsg, len, MSG_NOSIGNAL ) ; if ( rc > 0 ) { len = rc ; } else if ( rc == 0 ) { //PD_RC_CHECK ( DIY_NETWORK_CLOSE, PDWARNING, // "Peer unexpected shutdown" ) ; goto error; } else { rc = error ; goto error; } // Everything is fine when get here rc = DIY_OK ;done : return rc ;error : goto done ;}int _ossSocket::connect (){ int rc = DIY_OK ; rc = ::connect ( _fd, (struct sockaddr *)&_sockAddress, _addressLen ) ; if ( rc ) { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to connect, rc = %d", SOCKET_GETLASTERROR ) ; goto error; } rc = getsockname ( _fd, (sockaddr*)&_sockAddress, &_addressLen ) ; if ( rc ) { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to get local address, rc = %d", rc ) ; goto error; } // get peer address rc = getpeername ( _fd, (sockaddr*)&_peerAddress, &_peerAddressLen ) ; if ( rc ) { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to get peer address, rc = %d", rc ) ; goto error; }done : return rc ;error : goto done ;}void _ossSocket::close (){ if ( _init ) { int i = 0 ; //i = ::closesocket ( _fd ) ; i = ::close ( _fd ) ; _init = false ; }}int _ossSocket::accept ( int *sock, struct sockaddr *addr, socklen_t *addrlen, int timeout ){ int rc = DIY_OK ; int maxFD = (int)_fd ; struct timeval maxSelectTime ; fd_set fds ; maxSelectTime.tv_sec = timeout / 1000000 ; maxSelectTime.tv_usec = timeout % 1000000 ; while ( true ) { FD_ZERO ( &fds ) ; FD_SET ( _fd, &fds ) ; rc = select ( maxFD + 1, &fds, NULL, NULL, timeout>=0?&maxSelectTime:NULL ) ; // 0 means timeout if ( 0 == rc ) { *sock = 0 ; rc = DIY_TIMEOUT ; goto done ; } // if < 0, means something wrong if ( 0 > rc ) { rc = SOCKET_GETLASTERROR ; // if we failed due to interrupt, let's continue if ( EINTR == rc ) { continue ; } //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to select from socket, rc = %d", // SOCKET_GETLASTERROR); goto error; } // if the socket we interested is not receiving anything, let's continue if ( FD_ISSET ( _fd, &fds ) ) { break ; } } rc = DIY_OK ; *sock = ::accept ( _fd, addr, addrlen ); if ( -1 == *sock ) { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to accept socket, rc = %d", // SOCKET_GETLASTERROR ) ; goto error; }done : return rc ;error : close () ; goto done ;}int _ossSocket::disableNagle (){ int rc = DIY_OK ; int temp = 1 ; rc = setsockopt ( _fd, IPPROTO_TCP, TCP_NODELAY, (char *) &temp, sizeof ( int ) ) ; if ( rc ) { //PD_LOG ( PDWARNING, "Failed to setsockopt, rc = %d", SOCKET_GETLASTERROR ) ; return rc; } rc = setsockopt ( _fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &temp, sizeof ( int ) ) ; if ( rc ) { //PD_LOG ( PDWARNING, "Failed to setsockopt, rc = %d", SOCKET_GETLASTERROR ) ; return rc; } return rc ;}unsigned int _ossSocket::_getPort ( sockaddr_in *addr ){ return ntohs ( addr->sin_port ) ;}int _ossSocket::_getAddress ( sockaddr_in *addr, char *pAddress, unsigned int length ){ int rc = DIY_OK ; length = length < NI_MAXHOST ? length : NI_MAXHOST ; rc = getnameinfo ( (struct sockaddr *)addr, sizeof(sockaddr), pAddress, length, NULL, 0, NI_NUMERICHOST ) ; if ( rc ) { //PD_RC_CHECK ( DIY_NETWORK, PDERROR, // "Failed to getnameinfo, rc = %d", SOCKET_GETLASTERROR ) ; goto error; }done : return rc ;error : goto done ;}int _ossSocket::setAnsyn () //异步非阻塞{ return fcntl(_fd, F_SETFL, O_NONBLOCK | fcntl(_fd, F_GETFL, 0));}unsigned int _ossSocket::getLocalPort (){ return _getPort ( &_sockAddress ) ;}unsigned int _ossSocket::getPeerPort (){ return _getPort ( &_peerAddress ) ;}int _ossSocket::getLocalAddress ( char * pAddress, unsigned int length ){ return _getAddress ( &_sockAddress, pAddress, length ) ;}int _ossSocket::getPeerAddress ( char * pAddress, unsigned int length ){ return _getAddress ( &_peerAddress, pAddress, length ) ;}int _ossSocket::setTimeout ( int seconds ){ int rc = DIY_OK ; struct timeval tv ; tv.tv_sec = seconds ; tv.tv_usec = 0 ; // windows take milliseconds as parameter // but linux takes timeval as input rc = setsockopt ( _fd, SOL_SOCKET, SO_RCVTIMEO, ( char* ) &tv, sizeof ( tv ) ) ; if ( rc ) { //PD_LOG ( PDWARNING, "Failed to setsockopt, rc = %d", // SOCKET_GETLASTERROR ) ; return rc; } rc = setsockopt ( _fd, SOL_SOCKET, SO_SNDTIMEO, ( char* ) &tv, sizeof ( tv ) ) ; if ( rc ) { //PD_LOG ( PDWARNING, "Failed to setsockopt, rc = %d", // SOCKET_GETLASTERROR ) ; return rc; } return rc ;}int _ossSocket::getHostName ( char *pName, int nameLen ){ return gethostname ( pName, nameLen ) ;}int _ossSocket::getPort ( const char *pServiceName, unsigned short &port ){ int rc = DIY_OK ; struct servent *servinfo ; servinfo = getservbyname ( pServiceName, "tcp" ) ; if ( !servinfo ) port = atoi ( pServiceName ) ; else port = (unsigned short)ntohs(servinfo->s_port) ; return rc ;}
- diy数据库(二)--网络通信类
- Linux网络通信(二)
- Socket网络通信(二)
- diy数据库系列(引言)
- diy数据库(一)--概况
- diy数据库(十)--索引
- Wireshark之网络通信原理(二)
- socket网络通信<二>
- 自平衡机器人DIY(二)
- diy数据库(四)--锁和队列
- 数据库,界面,多线程,网络通信
- Android系统回顾(八):网络通信(二)
- Linux网络通信(二)Socket编写TCP/UDP
- OSMF的入门二(actionscript 网络通信API)
- Unity网络通信(二)Unity Network 聊天
- Linux网络通信(二)Socket编写TCP/UDP
- Unity网络通信(二)Unity Network 聊天
- Android的通信机制与网络(二)
- Cocos格子地图(1)
- 将 Ceph 存储集群集成到 OpenStack 云中
- Cocos格子地图(2)
- Linux内核分析:实验八--Linux进程调度与切换
- Redis笔记_6
- diy数据库(二)--网络通信类
- 一个绚丽的loading动效分析与实现!
- iOS 动画Animation-4-5: CALayer子类:CATransformLayer
- Cocos格子地图(3)
- 获取html页面中某个父元素下相同子元素的个数
- Altium Designer-Net has no driving source警告消除的方法
- 搭建自己的Git服务器
- 网络:提交 JSON 到服务器中
- 为什么十跪父母恩?