socket编程实现简单的TCP网络程序(下)
1.封装TCP socket
#include <iostream>
#include <string>
#include <stdio.h>
#include <cassert>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define CHECK_RET(q) if(!(q)) {return false;}
class TcpSocket {
public:
TcpSocket() : _fd(-1) {}
TcpSocket(int fd) :_fd(fd) {}
~TcpSocket(){}
//绑定地址信息-IPV4协议,字节流传输
bool Socket() {
_fd = socket(AF_INET, SOCK_STREAM, 0);
if (_fd < 0) {
perror("socket error~~\n");
return false;
}
return true;
}
//关闭套接字
bool Close() {
int ret = close(_fd);
if (ret < 0) {
perror("close error~~\n");
return false;
}
return true;
}
//为套接字绑定地址信息,地址为一个通用的结构体sockaddr
//结构体包含了协议类型,ip地址,port端口号
bool Bind(std::string& ip, uint16_t port) {
sockaddr_in addr;
addr.sin_family = AF_INET;
//inet_addr点分十进制字符串转换为网络ip(本质上是一个32字节数据)
addr.sin_addr.s_addr = inet_addr(ip.c_str());
//htons主机字节序转换为网络字节序
addr.sin_port = htons(port);
int ret = bind(_fd, (sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
perror("bind error~~\n");
return false;
}
return true;
}
//开始监听,num=5 最多允许5个客户端处于连接等待状态
bool Listen(int num = 5) {
int ret = listen(_fd, num);
if (ret < 0) {
perror("listen error~~\n");
return false;
}
return true;
}
//监听套接字用于接受连接请求,建立连接后会accept返回一个新的套接字
//这个套接字就是客户端建立的套接字,是真正用于进行通信的套接字
//而监听套接字不用于进行通信
//同时可以接收到对端的地址信息
bool Accept(TcpSocket& peer, std::string* ip = nullptr, uint16_t* port = nullptr) {
sockaddr_in addr;
socklen_t len = sizeof(addr);
int new_sock = accept(_fd, (sockaddr*)&addr, &len);
if (new_sock < 0) {
perror("accept error~~\n");
return false;
}
peer._fd = new_sock;
if (ip != nullptr) {
*ip = inet_ntoa(addr.sin_addr);
}
if (port != nullptr) {
*port = ntohs(addr.sin_port);
}
return true;
}
//请求建立连接,客户端使用
bool Connect(std::string& ip, uint16_t port) {
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
addr.sin_port = htons(port);
int ret = connect(_fd, (sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
perror("connect error~~\n");
return false;
}
return true;
}
//发送消息
bool Send(std::string& buf) {
int ret = send(_fd, buf.c_str(), buf.size(), 0);
if (ret < 0) {
perror("send error~~\n");
return false;
}
return true;
}
//接收消息
bool Recv(std::string& buf) {
char tmp[1024*4] = {0};
int len = recv(_fd, tmp, sizeof(tmp)-1, 0);
if (len < 0) {
perror("recv error~~\n");
return false;
}
if (len == 0) {
return false;
}
buf.assign(tmp, len);
return true;
}
int GetFd() {
return _fd;
}
private:
int _fd;
};
2.TCP通用服务器
#include "tcp_socket.hpp"
#include <functional>
typedef std::function<void (const std::string& req, std::string& resp)> Handler;
class TcpServer {
public:
TcpServer(std::string& ip, uint16_t port)
: _ip(ip)
, _port(port)
{}
~TcpServer() {}
bool Start(Handler handler) {
//1.创建套接字
CHECK_RET(sock.Socket());
//2.绑定地址信息
CHECK_RET(sock.Bind(_ip, _port));
//3.开始监听
CHECK_RET(sock.Listen());
//4.进入事件循环
while (true) {
//新的套接字,用于接受客户端的套接字
TcpSocket new_sock;
std::string ip;
uint16_t port;
//5.阻塞建立新连接
if(!sock.Accept(new_sock, &ip, &port))
continue;
printf("[ip-%s][port-%d] --> connect~~\n", ip.c_str(), port);
//6.对新建立的连接循环读写
while (true) {
//7.接收请求,失败跳出循环并且关闭新的连接
std::string req;
bool ret = new_sock.Recv(req);
if (!ret) {
printf("[ip-%s][port-%d] --> disconnect~~\n", ip.c_str(), port);
break;
}
std::cout << "请求:" << req << std::endl;
//8.计算响应
std::string resp;
handler(req, resp);
//9.发送响应
new_sock.Send(resp);
}
new_sock.Close();
}
return true;
}
private:
TcpSocket sock;
std::string _ip;
uint16_t _port;
};
3.英译汉服务器
#include "tcp_socket.hpp"
class TcpClient {
public:
TcpClient(std::string& ip, uint16_t port)
: _ip(ip)
, _port(port) {
assert(sock.Socket());
}
~TcpClient() {
assert(sock.Close());
}
bool Connect() {
return sock.Connect(_ip, _port);
}
bool Recv(std::string& req) {
return sock.Recv(req);
}
bool Send(std::string& resp) {
return sock.Send(resp);
}
private:
TcpSocket sock;
std::string _ip;
uint16_t _port;
};
4.TCP通用客户端
#include "tcp_server.hpp"
#include <unordered_map>
void Dict(const std::string& req, std::string& resp) {
std::unordered_map<std::string, std::string> dic;
dic.insert(std::make_pair("hello", "你好~~"));
dic.insert(std::make_pair("world", "世界~~"));
dic.insert(std::make_pair("shen", "珅~~"));
if (dic.find(req) != dic.end()) {
auto it = dic.find(req);
resp = it->second;
} else {
resp = "未找到~~";
}
}
// ./dict_server 127.0.0.1 9000
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "./dict_server 127.0.0.1 9000" << std::endl;
return -1;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpServer server(ip, port);
server.Start(Dict);
return 0;
}
5.英译汉客户端
#include "tcp_client.hpp"
// ./dict_client 127.0.0.1 9000
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "./dict_client 127.0.0.1 9000" << std::endl;
return -1;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpClient client(ip, port);
client.Connect();
while (true) {
std::string req;
std::cout << "请求:";
std::cin >> req;
client.Send(req);
std::string resp;
client.Recv(resp);
std::cout << "响应:" << resp << std::endl;
}
return 0;
}