socket操作封装
本文将unix中的socket相关操作进行封装,使得对socket的调用更加方便、直观
头文件SocketHel.h
#ifndef SOCKETSHEL__H__
#define SOCKETSHEL__H__
#include <arpa/inet.h>
#include <cstring> //memset
void setnbAndcoeHel(int socketfd);
int createNonblockingHel(int port);
void setnbAndcoeHel(int socketfd);
int createNonblockingHel(int port);
void bindHel(int socketfd,const struct sockaddr* addr);
void listenHel(int socketfd);
int acceptHel(int socketfd,struct sockaddr_in* addr);
int connectHel(int socketfd,const struct sockaddr* addr);
size_t readHel(int socketfd,void *buf,size_t count);
ssize_t readvHel(int socketfd,const struct iovec *iov,int iovcnt);
ssize_t writeHel(int socketfd,const void *buf,size_t count);
void closeHel(int socketfd);
void closeWriteHel(int socketfd);
void ipport_stctos(char *ip,size_t size,const struct sockaddr* addr);
void ip_stctos(char *ip,size_t size,const struct sockaddr* addr);
void ipport_stostc(const char *ip,uint16_t port,struct sockaddr_in* addr);
int getSocketError(int socketfd);
const struct sockaddr* sockaddr_cast(const struct sockaddr_in* addr);
const struct sockaddr_in* sockaddr_in_cast(const struct sockaddr* addr);
struct sockaddr* sockaddr_cast(struct sockaddr_in* addr);
struct sockaddr_in* sockaddr_in_cast(struct sockaddr* addr);
bool isSelfConnect(int socketfd);
struct sockaddr_in getLocalAddr(int socketfd);
struct sockaddr_in getPeerAddr(int socketfd);
源代码文件SocketHel.cpp
1) 用到的头文件
#include "SocketsHel.h"
#include "Types.h"
#include <cstring> // strlen,memset
#include <cassert> //assert
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdint.h>
#include <endian.h>
2) sockaddr* 和sockaddr_in* 的转化
const struct sockaddr* sockaddr_cast(const struct sockaddr_in* addr){
return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));
}
const struct sockaddr_in* sockaddr_in_cast(const struct sockaddr* addr){
return static_cast<const struct sockaddr_in*>(implicit_cast<const void*>(addr));
}
struct sockaddr* sockaddr_cast(struct sockaddr_in* addr){
return static_cast<struct sockaddr*>(implicit_cast<void*>(addr));
}
struct sockaddr_in* sockaddr_in_cast(struct sockaddr* addr){
return static_cast<struct sockaddr_in*>(implicit_cast<void*>(addr));
}
3) 设置套接字的FD_CLOEXEC标识和O_NOBLOCK标识
FD_CLOEXEC是为了在使用execl执行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。
O_NOBLOCK设置套接字为非阻塞模式,经常在ET模式下使用。
void setnbAndcoeHel(int socketfd){
int flags = ::fcntl(socketfd,F_GETFL,0);
flags |= O_NONBLOCK;
int res = ::fcntl(socketfd,F_SETFL,flags);
flags = ::fcntl(socketfd,F_GETFL,0);
flags |= FD_CLOEXEC;
res = ::fcntl(socketfd,F_SETFL,flags);
(void)res;
}
4) socket的创建和使用
int createNonblockingHel(int port){
port = ((port <= 1024) || (port >= 65535)) ? 6666:port;
//创建套接字
int socketfd = ::socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK ,0);
if(socketfd<0){
printf("SocketsHel::createNonblockingHel:%s\n",strerror(errno));
}
return socketfd;
}
void bindHel(int socketFd,const sockaddr* addr){
if((::bind(socketFd,addr,static_cast<socklen_t>(sizeof(sockaddr_in))))==-1){
printf("SocketsHel::createNonblockingHel fd=%d bind:%s\n",socketFd,strerror(errno));
}
}
void listenHel(int socketfd){
int res = ::listen(socketfd,1024);
if(res < 0){
printf("SocketsHel::istenHel:%s\n",strerror(errno));
}
printf("成功监听%d\n",socketfd);
}
int acceptHel(int socketfd,struct sockaddr_in* addr){
socklen_t addlen = static_cast<socklen_t>(sizeof *addr);
int accfd = ::accept(socketfd,sockaddr_cast(addr),&addlen);
setnbAndcoeHel(accfd);
if(accfd < 0){
//int savedErrno = errno;
printf("SocketsHel::acceptHel:%s\n",strerror(errno));
}
return accfd;
}
5) 请求连接,读取,写入,关闭操作
int connectHel(int socketfd,const struct sockaddr* addr){
return ::connect(socketfd,addr,static_cast<socklen_t>(sizeof(struct sockaddr_in)));
}
ssize_t readHel(int socketfd,void *buf,int count){
return ::read(socketfd,buf,count);
}
ssize_t readvHel(int socketfd,const struct iovec *iov,int iovcnt){
return ::readv(socketfd,iov,iovcnt);
}
ssize_t writeHel(int socketfd,const void *buf,size_t count){
return ::write(socketfd,buf,count);
}
void closeHel(int socketfd){
int res = ::close(socketfd);
if(res < 0){
printf("SocketsHel::closeHel:%s\n",strerror(errno));
}
}
void closeWriteHel(int socketfd){
int res = ::shutdown(socketfd,SHUT_WR);
if(res < 0){
printf("SocketsHel::closeWrite:%s\n",strerror(errno));
}
}
6) sockaddr_in 和char*以及uint16_t之间的转化
void ipport_stctos(char *buf,size_t size,const struct sockaddr* addr){
//ip_stctos(buf,size,addr);
size_t cur = ::strlen(buf);
const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);
uint16_t port = be16toh(addr4->sin_port);
assert(size > cur);
snprintf(buf+cur,size-cur,":%u",port);
}
void ip_stctos(char *buf,size_t size,const struct sockaddr* addr){
if(addr->sa_family == AF_INET){
assert(size >= INET_ADDRSTRLEN);
const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);
::inet_ntop(AF_INET,&addr4->sin_addr,buf,static_cast<socklen_t>(size));
}else if(addr->sa_family == AF_INET6){
printf("ip_stctos receives ip6\n");
}
}
void ipport_stostc(const char* ip,uint16_t port,struct sockaddr_in* addr){
addr->sin_family = AF_INET;
addr->sin_port = ::htons(port);
if(ip == "0.0.0.0" || ip=="127.0.0.1"){
addr->sin_addr.s_addr = ::htonl(INADDR_ANY);
}else{
if(::inet_pton(AF_INET,ip,&addr->sin_addr.s_addr) <= 0){
printf("ipport_stostc\n");
}
}
}
7) 获取套接字的错误代码
int getSocketError(int socketfd){
int optval;
socklen_t optlen = static_cast<socklen_t>(sizeof optval);
if(::getsockopt(socketfd,SOL_SOCKET,SO_ERROR,&optval,&optlen) < 0){
return errno;
}else{
return optval;
}
}
8) 获得本地以及对端的sockaddr_in
struct sockaddr_in getLocalAddr(int socketfd){
struct sockaddr_in localaddr;
memZero(&localaddr,sizeof localaddr);
socklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);
if(::getsockname(socketfd,sockaddr_cast(&localaddr),&addrlen) < 0){
printf("SocketsHel::getLocalAddr:%s\n",strerror(errno));
}
return localaddr;
}
struct sockaddr_in getPeerAddr(int socketfd){
struct sockaddr_in peeraddr;
memZero(&peeraddr,sizeof peeraddr);
socklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);
if(::getpeername(socketfd,sockaddr_cast(&peeraddr),&addrlen) < 0){
printf("SocketsHel::getPeerAddr:%s\n",strerror(errno));
}
return peeraddr;
}
9) 是否发生了自连接
bool isSelfConnect(int socketfd){
struct sockaddr_in localaddr = getLocalAddr(socketfd);
struct sockaddr_in peeraddr = getPeerAddr(socketfd);
if(localaddr.sin_family == AF_INET){
const struct sockaddr_in* laddr4 = reinterpret_cast<struct sockaddr_in*>(&localaddr);
const struct sockaddr_in* paddr4 = reinterpret_cast<struct sockaddr_in*>(&peeraddr);
return laddr4->sin_port == paddr4->sin_port && laddr4->sin_addr.s_addr == paddr4->sin_addr.s_addr;
}
return false;
}