项目完整版在:
一、socket模块:套接字模块
二、提供的功能
Socket模块是对套接字操作封装的一个模块,主要实现的socket的各项操作。
socket 模块:套接字的功能
创建套接字
绑定地址信息
开始监听
向服务器发起连接
获取新连接
接受数据
发送数据
关闭套接字
创建一个监听链接
创建一个客户端连接
设置套接字选项——开启地址端口重用!
设置套接字阻塞属性——设置为非阻塞!
三、实现思想
(一)功能
对socket套接字的操作进行封装。
(二)意义
对socket套接字的操作进行封装。
(三)功能设计
- 创建套接字
- 绑定地址信息
- 开始监听
- 向服务器发起连接
- 获取新连接
- 接受数据
- 发送数据
- 关闭套接字
- 创建一个监听链接
- 创建一个客户端连接
四、代码
#define MAX_LISTEN 1024
class Socket {
private:
int _sockfd;
public:
Socket() :_sockfd(-1) {}
Socket(int fd) : _sockfd(fd) {}
~Socket() {Close(); }
int fd() {return _sockfd;}
// 1.创建套接字
bool Create() {
//int socket (int domain,int type,int protocol);
_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (_sockfd < 0) {
ERR_LOG("CREATE SOCKET FAILED !!");
return false;
}
return true;
}
// 2.绑定地址信息
bool Bind(const std::string &ip,uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(struct sockaddr_in);
// int bind(int sockfd,struct sockaddr * addr,socklen_t len);
int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
if (ret < 0) {
ERR_LOG("BIND ADDRESS FAILED!!!!");
return false;
}
return true;
}
// 3.开始监听
bool Listen(int backlog = MAX_LISTEN) {
// int listen(int backlog)
int ret = listen(_sockfd,backlog);
if (ret < 0) {
ERR_LOG("SOCKET LISTEN FAILED!!");
return false;
}
return true;
}
// 4. 向服务器发起连接
bool Connect(const std:: string& ip,uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(struct sockaddr_in);
// int bind(int sockfd,struct sockaddr * addr,socklen_t len);
int ret = connect(_sockfd,(struct sockaddr*)&addr,len);
if (ret < 0) {
ERR_LOG("CONNECT ADDRESS FAILED!!!!");
return false;
}
return true;
}
// 5. 获取新连接
int Accept() {
// int accept(int sockfd,struct sockaddr* addr,socklen_t *len) /
int newfd = accept(_sockfd,NULL,NULL);
if (newfd < 0) {
ERR_LOG("SOCKET ACCEPT FAILED!!!!");
return false;
}
return newfd;
}
ssize_t Recv(void *buf,size_t len,int flag = 0){
// 6.接收数据
//有符号长整型
//ssize_t Recv(int sockfd,void* buf,size_t len,int flag);
ssize_t ret = recv(_sockfd,buf,len,flag);
if (ret <= 0) {
// EAGAIN 当前socket的接收缓冲区没有数据来,在非阻塞二点情况下才会有这个错误
// ENTER 当前socket的阻塞等待,被信号打断了
if (errno == EAGAIN || errno == EINTR) {
return 0; // 没收到数据
}
ERR_LOG("SOCKET RECV FAILED!!");
return -1; // 出错
}
return ret;
}
ssize_t nonBlockRecv(void* buf,size_t len) {
return Recv(buf,len,MSG_DONTWAIT); // MSG_DONTWAIT 表示当前接受为非阻塞
}
// 7.发送数据
ssize_t Send(const void* buf,size_t len,int flag = 0) {
// ssize_t send(int sockfd,void *data,size_t len,int flag)
ssize_t ret = send(_sockfd,buf,len,flag);
if (ret < 0) {
ERR_LOG("SOCKET SEND FAILED!!");
return -1; // 出错
}
return ret; // 实际发送数据长度!!
}
ssize_t nonBlockSend(void* buf,size_t len) {
return Send(buf,len,MSG_DONTWAIT); // MSG_DONTWAIT 表示当前接受为非阻塞
}
// 8.关闭套接字
void Close() {
if (_sockfd != -1) {
close(_sockfd);
_sockfd = -1;
}
}
// 9.创建一个服务端链接
bool createServer(uint16_t port, const std::string &ip = "0.0.0.0", bool block_flag = false) {
// 1.创建套接字 2. 绑定地址 3.开始监听 4.设置非阻塞 5.启动地址重用
if (Create() == false) return false;
if (Bind(ip,port) == false) return false;
if (Listen() == false) return false;
if (block_flag) NonBlock();
ReuseAddress();
return true;
}
// 10.创建一个客户端链接
bool createClient(uint16_t port, const std::string &ip) {
if (Create() == false) return false;
if (Connect(ip,port) == false) return false;
return true;
}
// 11. 设置套接字选项——开启地址端口重用!
void ReuseAddress() {
// int setsockopt(int fd,int leve,int optname,void *val,int vallen)
int val = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));
val = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));
}
// 12. 设置套接字阻塞属性——设置为非阻塞!
void NonBlock() {
int flag = fcntl(_sockfd, F_GETFL, 0);
fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
}
};
五、测试
(一)tcp_cli.cc
#include "../source/server.hpp"
int main() {
Socket cli_sock;
cli_sock.createClient(8500,"127.0.0.1");
std::string str = "nihao";
cli_sock.Send(str.c_str(),str.size());
char buf[1024] = {0};
cli_sock.Recv(buf,1023);
DBG_LOG("%s",buf);
return 0;
}
(二)tcp_srv.cc
#include "../source/server.hpp"
int main() {
Socket lst_sock;
bool ret = lst_sock.createServer(8500);
while (1) {
int newfd = lst_sock.Accept();
if (newfd < 0) {
continue;
}
Socket cli_sock(newfd);
char buf[1024] = {0};
int ret = cli_sock.Recv(buf,1023);
if(ret < 0) {
cli_sock.Close();
}
cli_sock.Send(buf,ret);
cli_sock.Close();
}
lst_sock.Close();
return 0;
}
(三)makefile
all:client server
client:tcp_cli.cc
g++ -std=c++11 $^ -o $@
server:tcp_srv.cc
g++ -std=c++11 $^ -o $@