本篇博客定义一套用于TCP通信比较实用/好用Socket类库(运用C++封装的思想,将socket API尽量封装的好用与实用), 从开发出Socket库的第一个版本以来, 作者不知道做了多少改进, 每次有新的/好的想法尽量实现到该库当中来; 而且我还使用该库开发出作者第一个真正意义上的基于Linux的Server程序[MyHttpd, 在后续的博客当中, 我一定会将MyHttpd的实现原理与实现代码更新到这篇博客所属的专栏中, 希望读者朋友不吝赐教];

可能在以后的学习和工作中, 作者还可能会加入新的功能和修复发现的BUG, 因此, 如果有读者喜欢这个Socket库, 请持续关注这篇博客, 我会把最新的更新信息都发布到这篇博客当中, 当然, 如果有读者朋友发现了这个Socket库的BUG, 还希望读者朋友不吝赐教, 谢谢您的关注;

实现中的几个注意点:

1)TCPSocket类几个成员函数的访问权限为protected, 使Socket类可以进行继承,但不允许私自使用;

2)TCPClient类的send/receive方法使用了著名的writen/readn(来源UNP)实现, 解决了TCP的粘包问题.

3)TCPServer端添加了地址复用, 可以方便TCP服务器重启;

4)添加了异常类,让我们在编写易出错的代码时,可以解放思想,不用一直考虑该函数调用出错会发生什么情况!

5)TCPSocket类中添加了getfd接口, 如果有这三个类完成不了的功能, 则可以将socket获取出来, 使用Linux的系统调用完成相应的功能;

6)TCPClient中有好几个发送/接受的接口, 其中, 使用send发送的数据一定要使用receive来接收, 因为作者使用的是自定义应用层协议来解决的TCP粘包问题, 请读者朋友注意;

由于实现思想较简单, 因此在代码中并未添加大量的注释, 请读者耐心读下去, 在博文结尾处, 会有该库的测试使用示例与Makefile文件, 并将整个文件夹(完整的项目实现源代码)放到了CSDN的下载资源(不需要下载分的O(∩_∩)O~)中, 下载链接见下:

http://download.csdn.net/detail/hanqing280441589/8486489

Socket类

TCPSocket/TCPClient/TCPServer类设计

class TCPSocket
{
protected:TCPSocket();virtual ~TCPSocket();bool create();bool bind(unsigned short int port, const char *ip = NULL) const;bool listen(int backlog = SOMAXCONN) const;bool accept(TCPSocket &clientSocket) const;bool connect(unsigned short int port, const char *ip) const;/**注意: TCPSocket基类并没有send/receive方法**/bool reuseaddr() const;bool isValid() const{return (m_sockfd != -1);}
public:bool close();int getfd() const{return m_sockfd;}//flag: true=SetNonBlock, false=SetBlockbool setNonBlocking(bool flag) const;protected:int m_sockfd;
};
/** TCP Client **/
class TCPClient : public TCPSocket
{
private:struct Packet{unsigned int    msgLen;     //数据部分的长度(网络字节序)char            text[1024]; //报文的数据部分};
public:TCPClient(unsigned short int port, const char *ip) throw(SocketException);TCPClient();TCPClient(int clientfd);~TCPClient();size_t send(const std::string& message) const throw(SocketException);size_t receive(std::string& message) const throw(SocketException);size_t read(void *buf, size_t count) throw(SocketException);void   write(const void *buf, size_t count) throw(SocketException);size_t write(const char *msg) throw(SocketException);
};
/** TCP Server **/
class TCPServer : public TCPSocket
{
public:TCPServer(unsigned short int port, const char *ip = NULL, int backlog = SOMAXCONN) throw(SocketException);~TCPServer();void accept(TCPClient &client) const throw(SocketException);TCPClient accept() const throw(SocketException);
};

TCPSocket/TCPClient/TCPServer类实现

TCPSocket::TCPSocket(): m_sockfd(-1) {}
TCPSocket::~TCPSocket()
{if (isValid())::close(m_sockfd);
}bool TCPSocket::create()
{if (isValid())return false;if ((m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)return false;return true;
}bool TCPSocket::bind(unsigned short int port, const char *ip) const
{if (!isValid())return false;struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);if (ip == NULL)addr.sin_addr.s_addr = htonl(INADDR_ANY);elseaddr.sin_addr.s_addr = inet_addr(ip);if ( ::bind(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1 )return false;return true;
}
bool TCPSocket::listen(int backlog) const
{if (!isValid())return false;if ( ::listen(m_sockfd, backlog) == -1)return false;return true;
}
bool TCPSocket::accept(TCPSocket &clientSocket) const
{if (!isValid())return false;clientSocket.m_sockfd =::accept(this->m_sockfd, NULL, NULL);if (clientSocket.m_sockfd == -1)return false;return true;
}bool TCPSocket::connect(unsigned short int port, const char *ip) const
{if (!isValid())return false;struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip);if ( ::connect(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)return false;return true;
}bool TCPSocket::setNonBlocking(bool flag) const
{if (!isValid())return false;int opt = fcntl(m_sockfd, F_GETFL, 0);if (opt == -1)return false;if (flag)opt |= O_NONBLOCK;elseopt &= ~O_NONBLOCK;if (fcntl(m_sockfd, F_SETFL, opt) == -1)return false;return true;
}
bool TCPSocket::reuseaddr() const
{if (!isValid())return false;int on = 1;if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)return false;return true;
}
bool TCPSocket::close()
{if (!isValid())return false;::close(m_sockfd);m_sockfd = -1;return true;
}
/** client TCP Socket **/
TCPClient::TCPClient(unsigned short int port, const char *ip)
throw(SocketException)
{if (create() == false)throw SocketException("tcp client create error");if (connect(port, ip) == false)throw SocketException("tcp client connect error");
}
TCPClient::TCPClient() {}
TCPClient::TCPClient(int clientfd)
{m_sockfd = clientfd;
}
TCPClient::~TCPClient() {}
/** client端特有的send/receive **/
static ssize_t readn(int fd, void *buf, size_t count);
static ssize_t writen(int fd, const void *buf, size_t count);//send
size_t TCPClient::send(const std::string& message)
const throw(SocketException)
{Packet buf;buf.msgLen = htonl(message.length());strcpy(buf.text, message.c_str());if (writen(m_sockfd, &buf, sizeof(buf.msgLen)+message.length()) == -1)throw SocketException("tcp client writen error");return message.length();
}
//receive
size_t TCPClient::receive(std::string& message)
const throw(SocketException)
{//首先读取头部Packet buf = {0, 0};size_t readBytes = readn(m_sockfd, &buf.msgLen, sizeof(buf.msgLen));if (readBytes == (size_t)-1)throw SocketException("tcp client readn error");else if (readBytes != sizeof(buf.msgLen))throw SocketException("peer connect closed");//然后读取数据部分unsigned int lenHost = ntohl(buf.msgLen);readBytes = readn(m_sockfd, buf.text, lenHost);if (readBytes == (size_t)-1)throw SocketException("tcp client readn error");else if (readBytes != lenHost)throw SocketException("peer connect closed");message = buf.text;return message.length();
}
size_t TCPClient::read(void *buf, size_t count) throw(SocketException)
{ssize_t readBytes = ::read(m_sockfd, buf, count);if (readBytes == -1)throw SocketException("tcp client read error");return (size_t)readBytes;
}
void TCPClient::write(const void *buf, size_t count) throw(SocketException)
{if ( ::write(m_sockfd, buf, count) == -1 )throw SocketException("tcp client write error");
}
size_t TCPClient::write(const char *msg) throw(SocketException)
{if ( ::write(m_sockfd, msg, strlen(msg)) == -1 )throw SocketException("tcp client write error");return strlen(msg);
}
/** Server TCP Socket**/
TCPServer::TCPServer(unsigned short int port, const char *ip, int backlog)
throw(SocketException)
{if (create() == false)throw SocketException("tcp server create error");if (reuseaddr() == false)throw SocketException("tcp server reuseaddr error");if (bind(port, ip) == false)throw SocketException("tcp server bind error");if (listen(backlog) == false)throw SocketException("tcp server listen error");
}
TCPServer::~TCPServer() {}
void TCPServer::accept(TCPClient &client) const
throw(SocketException)
{//显式调用基类TCPSocket的acceptif (TCPSocket::accept(client) == -1)throw SocketException("tcp server accept error");
}
TCPClient TCPServer::accept() const
throw(SocketException)
{TCPClient client;if (TCPSocket::accept(client) == -1)throw SocketException("tcp server accept error");return client;
}
/** readn/writen实现部分 **/
static ssize_t readn(int fd, void *buf, size_t count)
{size_t nLeft = count;ssize_t nRead = 0;char *pBuf = (char *)buf;while (nLeft > 0){if ((nRead = read(fd, pBuf, nLeft)) < 0){//如果读取操作是被信号打断了, 则说明还可以继续读if (errno == EINTR)continue;//否则就是其他错误elsereturn -1;}//读取到末尾else if (nRead == 0)return count-nLeft;//正常读取nLeft -= nRead;pBuf += nRead;}return count;
}
static ssize_t writen(int fd, const void *buf, size_t count)
{size_t nLeft = count;ssize_t nWritten = 0;char *pBuf = (char *)buf;while (nLeft > 0){if ((nWritten = write(fd, pBuf, nLeft)) < 0){//如果写入操作是被信号打断了, 则说明还可以继续写入if (errno == EINTR)continue;//否则就是其他错误elsereturn -1;}//如果 ==0则说明是什么也没写入, 可以继续写else if (nWritten == 0)continue;//正常写入nLeft -= nWritten;pBuf += nWritten;}return count;
}

SocketException类

//SocketException类的设计与实现
class SocketException
{
public:typedef std::string string;SocketException(const string &_msg = string()): msg(_msg) {}string what() const{if (errno == 0)return msg;//如果errno!=0, 则会加上错误描述return msg + ": " + strerror(errno);}private:string msg;
};

Socket类测试(echo)

Server端测试代码

void sigHandler(int signo)
{while (waitpid(-1, NULL, WNOHANG) > 0);
}int main()
{signal(SIGCHLD, sigHandler);signal(SIGPIPE, SIG_IGN);try{TCPServer server(8001);std::string msg;while (true){TCPClient client = server.accept();pid_t pid = fork();if (pid == -1)err_exit("fork error");else if (pid > 0)client.close();else if (pid == 0){try{while (true){client.receive(msg);cout << msg << endl;client.send(msg);}}catch (const SocketException &e){cerr << e.what() << endl;exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}}}catch (const SocketException &e){cerr << e.what() << endl;exit(EXIT_FAILURE);}
}

Client端测试代码

int main()
{signal(SIGPIPE, SIG_IGN);try{TCPClient client(8001, "127.0.0.1");std::string msg;while (getline(cin, msg)){client.send(msg);msg.clear();client.receive(msg);cout << msg << endl;msg.clear();}}catch (const SocketException &e){cerr << e.what() << endl;}
}

Makefile

.PHONY: clean all
CC = g++
CPPFLAGS = -Wall -g -pthread -std=c++11
BIN = serverclient
SOURCES = $(BIN.=.cpp)all: $(BIN)
$(BIN): $(SOURCES) Socket.cppclean:-rm -rf $(BIN) bin/ obj/ core

Socket编程实践(7) --Socket-Class封装(改进版v2)相关推荐

  1. Socket编程实践(3) --Socket API

    socket函数 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, ...

  2. Socket编程实践(2) --Socket编程导引

    什么是Socket? Socket可以看成是用户进程与内核网络协议栈的接口(编程接口, 如下图所示), 其不仅可以用于本机进程间通信,可以用于网络上不同主机的进程间通信, 甚至还可以用于异构系统之间的 ...

  3. Socket编程实践(10) --select的限制与poll的使用

    select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...

  4. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  5. C# socket编程实践——支持广播的简单socket服务器

    在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...

  6. 使用C++基于Socket编程实现文件下载(改进-封装成类)

    使用: (1)首先运行服务端,待服务端运行起来: (2)最后运行客户端,输入要传输文件到哪个目标机器的IP地址: (3)输入传输文件的路径及文件(完成的路径),其中包含文件的类型,也就是后缀需要包含( ...

  7. socket编程基础2(socket API函数介绍)

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

  8. 计算机网络实验socket编程,计算机网络实验 socket编程

    使用连接.终止连接的过程.在数据传输过程中,各数据分组不携带目的地址,而使用连接号(connect ID ).从本质上看,连接是一个管道,收发数据不但顺序一致,而且内容相同. Socket 编程中,双 ...

  9. socket编程 —— 非阻塞socket (转)---例子已上传至文件中

    在上一篇文章 <socket编程--一个简单的例子> http://blog.csdn.net/wind19/archive/2011/01/21/6156339.aspx 中写了一个简单 ...

  10. Socket编程概念和 Socket之异步TCP客户端断线重连

    一:什么是SOCKET​​​​​​​ socket的英文原义是"孔"或"插座".作为进程通信机制,取后一种意思.通常也称作"套接字",用于描 ...

最新文章

  1. 第28篇 js中let和var
  2. 大数据系列修炼-Scala课程07
  3. tableau可视化数据分析60讲(一)-tableau概念性叙述及环境配置
  4. MAC电脑常用效率工具推荐
  5. bootstrap样式
  6. java和cnc_Java程序员的目标,你都达到了多少条?
  7. html lineheight div,html – Chrome上的文本输入:line-height似乎有最小值
  8. 模拟任务调度算法 C语言 【留学生作业】
  9. 【计算机系统组成】IO设备知识要点
  10. 《计算机网络教程》(微课版 第五版)复习补充题
  11. 51单片机电子制作DIY-----交通灯控制器
  12. PHP FastCGI RCE Vul
  13. Java 输入判断5,7倍数
  14. Raspberry Pi 3安装配置Raspbian过程
  15. Unity UI界面的设计(完整版)
  16. 简单的java文档扫描器
  17. 链表_两两交换链表中的节点
  18. 计算机科学与物流工程国际学术会议,第一届通信工程与物流管理国际会议
  19. c++ 高精度 加减乘除 四则运算 代码实现
  20. 应届实习生 北京短租租房记

热门文章

  1. (计算机组成原理)第五章中央处理器-第一节:CPU的功能与基本结构(运算器和控制器等寄存器)
  2. CentOS 6、7 安装 Golang
  3. Python判断两个域名的主域名是否相同
  4. PySpider问题记录http599
  5. ReportViewer中设置ServerReport.ReportServerCredentials属性的方法(WEB版)
  6. Problem 25
  7. error: Microsoft Visual C++ 14.0 is required.
  8. YAML_11 when条件判断
  9. 缩点【洛谷P1262】 间谍网络
  10. Elizabeth Taylor【伊丽莎白·泰勒】