socket网络服务实战
一、epoll模型的构建
由于网络服务高并发的需求,一般socket网络模型都采用epoll模型,有关epoll模型的原理在相关论坛中有许多讲述,在此不做重复讲解,主要讲一讲epoll模型的封装实现。
EPoller类的具体实现如下代码所示:
class EPoller
{
public: int Create(int maxfd);
void JoinSocket(Socket* sock, unsigned int flag);
void DelSocket(CEpollSocket* sock);
int LoopForEvent(int timeout);
int EpollWait(int timeout);
protected:
int epoll_fd; //epoll文件描述符
epoll_event* p_events; //用于保存epoll_wait返回的事件
int max_fd; //最大监听的文件描述符数量 }; // 创建epoll模型
int EPoller::Create(int maxfd)
{
max_fd = maxfd ;
epoll_fd = epoll_create(maxfd); p_events = new epoll_event[maxfd];
memset(p_events, , sizeof(epoll_event) * maxfd);
return ;
} //将某个fd添加至监听的队列中,并利用flag设置监听的事件类型
//其中Socket为基类
int EPoller::JoinSocket(Socket* sock, unsigned int flag)
{
int fd = sock->GetSockHandle();
epoll_event event;
event.data.ptr = sock;
event.events = flag|EPOLLHUP|EPOLLERR; if (epoll_ctl(_epoll_fd, EPOLL_CTL_MOD , fd, &ev) < )
{
if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD , fd, &ev) < )
{
return -;
}
} return ;
} //删除指定的fd
int EPoller::DelSocket(Socket* sock)
{
int fd = sock->GetSockHandle();//利用Socket对象取得相应的文件句柄
if ( fd > )
{
epoll_event ev;
ev.data.ptr = sock;
ev.events = ;
if (epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, &ev) < )
{
return -;
} return ;
} return -;
} //等待监控的事件触发
int EPoller::EpollWait(int timeout)
{
int fd;
int nfds;
Socket* sock;
unsigned int flag; nfds = epoll_wait(epoll_fd, p_events, max_fd, timeout);
if (nfds < )
{
return -;
}
else if ( nfds == )
{
return ;
} for (int i=; i<nfds; i++)
{
sock = (Socket*)((p_events+i)->data.ptr);
if ( sock == NULL )
{
continue;
}
flag = (p_events+i)->events;
if (flag & EPOLLIN)
{
sock->Recv();
}
else if (flag & EPOLLOUT)
{
sock->Send();
}
else if (flag & EPOLLHUP)
{
sock->Close();
}
else if (flag & EPOLLERR)
{
sock->Error();
}
} return nfds ;
}
// 循环等待事件的触发
int EPoller::LoopForEvent(int timeout)
{
while(true)
{
int result = EpollWait(timeout);
if( result <= )
{
return result;
}
}
}
其中的Socket类型作为其他网络通信服务类的基类,主要为其他的socket网络通信类提供一个统一的入口,其结构定义如下:
class Socket
{
friend class EPoller;
public:
int GetSockHandle();
virtual int SendMsg(char* buffer, int length); protected:
virtual int Recv();
virtual int Send();
virtual int Close();
virtual int Error(); EPoller* epoller;
int fd; } int Socket::SendMsg(char* buff, int len)
{
int ret;
int send_len = 0;
int _len = len;
errno = 0;
while(_len > 0)
{
ret = send(fd, (void*)(buff+send_len), _len, 0);
if (ret <= 0)
{
if (errno == EINTR || errno == EAGAIN)
{
usleep(10);
continue;
}
break;
}
send_len += ret;
_len -= ret;
}
return send_len;
}
二、UDP网络通信的实现
//Udp创建socket的流程
int CreateUdpSocket()
{
// 1.创建socket
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // 2.设置非阻塞
fcntl(sock_fd, F_GETFL, );
fcntl(sock_fd, F_SETFL, val | O_NONBLOCK); // 3.绑定端口与ip
struct sockaddr_in addr;
string ip = "0.0.0.0";
addr.sin_family = AF_INET;
addr.sin_port = htons();
ip2uint(ip.c_str(), &(addr.sin_addr));
bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); // 4.设置socket缓存大小
int recv_buf_size = ;
int send_buf_size = ;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size));
setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size));
return ;
} // 接收数据
if ((len = recvfrom(sock, buffer, len, , (struct sockaddr *)&addr, &addrlen)) < )
{
if (errno == EAGAIN || errno == EINTR)
{
len = ;
return ;
}
else
{
return -;
}
}
三、TCP网络通信的实现
//Tcp创建
bool CreateTcpServer()
{
//建立TCP套接字
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in svr_addr= {};
svr_addr.sin_port = htons(port);
svr_addr.sin_family = AF_INET;
inet_pton(AF_INET, ip.c_str(), &(svr_addr.sin_addr)); // 绑定ip地址
if(bind(sock_fd, (struct sockaddr*)&svr_addr, sizeof(struct sockaddr_in)) < ) // 设置socket属性
int recv_buf_size = * ;
int send_buf_size = * ;
if (setsockopt(_sock_fd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buf_size, sizeof(recv_buf_size)) < )
if(setsockopt(_sock_fd, SOL_SOCKET, SO_SNDBUF, (char*)&send_buf_size, sizeof(send_buf_size)) < )
int reuse=;
if(setsockopt(_sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < ) // 设置非阻塞
int val = fcntl(_sock_fd, F_GETFL, );
if(fcntl(_sock_fd, F_SETFL, val | O_NONBLOCK) == -) // 监听socket
if(listen(sock_fd,) < )
{
close(sock_fd);
sock_fd = -;
return false;
}
return true ;
}
// 一种hash值的生成算法
int string2hash(const char * str, const int& len, const int& hash_size)
{
int hashvalue = ;
for (int i = len-; i < len; ++i)
{
hashvalue = ((unsigned char)(*(str + i)) + hashvalue) % hash_size;
}
return hashvalue;
} //接收数据流程
int TCP_recv()
{
char recv_buff[];
int total_recv_len = ; int clientfd = accept(_sock_fd,(struct sockaddr *)NULL,(socklen_t*)NULL);
if (clientfd <= )
{
return -;
} //设置发送缓冲区和接收缓冲区大小
int send_buff_size = ;
int recv_buff_size = ;
int set_result = ;
set_result = setsockopt(clientfd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buff_size, sizeof(recv_buff_size));
set_result = setsockopt(clientfd, SOL_SOCKET, SO_SNDBUF, (char*)&send_buff_size, sizeof(send_buff_size)); int free_len = sizeof(recv_buff) - total_recv_len ;
if(free_len <= || total_recv_len >= )
{
//包太长了
return ;
} //接收数据
int recv_len = ;
do
{
recv_len = recv(sock_fd, &recv_buff[total_recv_len], free_len, );
}
while((recv_len < ) && (errno == EINTR)); if(recv_len <= )
{
// 对端连接关闭
if( recv_len == )
{
return ;
}
else if( errno == EAGAIN)
{
//暂时阻塞
return ;
}
else
{
return ;
}
} total_recv_len += recv_len;
// 解析命令
int offset = ParseCmd(recv_buff, total_recv_len);
if( == offset)
{
//未接收完
return ;
}
// 完成解析后,将已处理后的数据清空,使留下的未处理数据移到缓存区的起始位置。
else if( < offset)
{
//解析成功,单条命令
total_recv_len = total_recv_len - offset;
memmove(recv_buff, recv_buff + offset, total_recv_len);
} return ; } // 与服务端建立socket长连接的方法
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int recv_buf_size = ;
int send_buf_size = ; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size));
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size)); struct sockaddr_in addr;
memset(&addr, , sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str()); int argc = fcntl(fd, F_GETFL, );
if (fcntl(fd, F_SETFL, argc | O_NONBLOCK) == -)
{
close(fd);
return false;
} int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if ( > ret)
{
bool bConnected = false; struct pollfd conncet_client[];
int _nfd = ;
memset(&_conncet_client[], , sizeof(pollfd));
conncet_client[].fd = fd;
conncet_client[].events = POLLOUT; int can_write = ::poll(_conncet_client, _nfd, (int)());
if (can_write > && (_conncet_client[].revents & POLLOUT) > )
{
bConnected = true;
}
}
socket网络服务实战的更多相关文章
- 【linux高级程序设计】(第十三章)Linux Socket网络编程基础 4
网络调试工具 tcpdump 功能:打印指定网络接口中与布尔表达式匹配的报头信息 关键字: ①类型:host(默认).net.port host 210.27.48.2 //指明是一台主机 net 2 ...
- Socket网络编程(2)--服务端实现
中秋了,首先祝大家中秋快乐,闲着无事在家整一个socket的聊天程序,有点仿QQ界面,就是瞎折腾,不知道最后是不是能将所有功能实现. 如果你对socket不了解,请看这篇文章:http://www.c ...
- keepalived安装配置实战心得(实现高可用保证网络服务不间断)
keepalived安装配置实战心得(实现高可用保证网络服务不间断) 一.准备2台虚拟机 安装的系统是:centos-release-7-1.1503.el7.centos.2.8.x86_6 ...
- socket 网络编程高速入门(一)教你编写基于UDP/TCP的服务(client)通信
由于UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,由于这些函数的结构往往比較复杂,參数大 ...
- Socket网络编程-SocketServer
Socket网络编程-SocketServer 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.SocketServer概述 socket编程过于底层,编程虽然有套路,但是想要写 ...
- Socket网络编程-TCP编程
Socket网络编程-TCP编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.socket介绍 1>.TCP/IP协议 2>.跨网络的主机间通讯 在建立通信连接的 ...
- SSH远程管理服务实战
目录 SSH远程管理服务实战 1.SSH基本概述 2.SSH相关命令 3.Xshell连接不上虚拟机 4.scp命令 5.sftp命令 6.SSH验证方式 7.SSH场景实践 8.SH安全优化 9.交 ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
- Python Socket 网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
随机推荐
- 最终还是选择了markdownpad2
markdownpad2使用 最终 哈哈,最后还是选择了markdownpad2,经过探索才知道这个玩意多么好用. 点击,下载. 碰到的问题 1.win10出现HTML无法渲染得对话框 结果是,官网有 ...
- systemd详解(CentOS 7)
http://blog.51cto.com/xuding/1730952 一.init进程演变 1.init的发展 CentOS 5: SysV init,串行 CentOS 6:Upstart,并行 ...
- 解决ListView 缓存机制带来的显示不正常问题
ListView加载数据原理:系统绘制ListView时,首先会用getCount()函数得到要绘制的这个列表的长度,然后开始逐行绘制.然后调用getView()函数,在这个函数里面首先获得一个Vie ...
- Day2-Python基础2---字符编码与转码
详细内容http://www.cnblogs.com/yuanchenqi/articles/5956943.html 一.编码介绍: 1.基本概念: 在python 2中默认编码是 ASCII,而在 ...
- spring 学习二 @RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. RequestMapping注解有六个属性,下面我们把她 ...
- ManualResetEvent 用法
第一.简单介绍 ManualResetEvent 允许线程通过发信号互相通信.通常,此通信涉及一个线程在其他线程进行之前必须完成的任务.当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时, ...
- ghld data format
%CTF: 1.00%FileType: PROF strp "VelocityProfile"%PROFSpec: 1.00 2006 00 00%Manufacturer: C ...
- FireMoneky 菜单
FireMoneky 菜单 TPopup是容器,里边摆放一排button也可. TPopup + ListBox 也可以用弹出窗体实现,form上放一排按钮,功效类似,但是form在fmx下不能半屏显 ...
- DAY15-HTTP协议简述
HTTP协议 一.HTTP协议简介 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式.协作式和超媒体信息系统的应用层协议.HTTP是万维网 ...
- CentOS6.5 安装ORACLE 安装界面乱码解决方案
在终端运行 export LANG=EN_US 然后再执行安装程序