文章目录
- Tcp协议
- Tcp协议常见API接口
- 1. int socket(int domain, int type, int protocol);
- 2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);
- 3. int listen(int socket, int backlog);
- 4. int accept(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);
- 5.int connect(int socket, const struct sockaddr *address,socklen_t address_len);
- telnet通信
- 服务器代码
- 客户端代码
- 翻译效果图
Tcp协议
简单了解一下Tcp协议,他与Udp协议都是传输层协议,而他与Udp协议的区别就是Tcp是有连接、可靠传输并且是面向字节流的一种协议。
Tcp协议常见API接口
前两个接口与Udp协议用法一样,只不过将申请套接字的SOCK_DGRAM改为了面向字节流的SOCK_STREAM。
1. int socket(int domain, int type, int protocol);
创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
参数 int domin :指定要在其中创建套接字的通信域。这里我们使用AF_INET采用IPV4通信域。
参数 int type : 指定要创建的套接字类型。 Tcp协议是采用面向字节流,所以是用SOCK_STREAM。
参数 int protocol :指定与套接字一起使用的特定协议。
返回值: 如果成功则返回创建的socket 文件描述符, 失败则返回-1.
代码示例
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1)
{
exit(1);
}
2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);
绑定端口号 (TCP/UDP, 服务器)
参数 int socket 就是使用socket接口函数所创建的那个socket文件描述符。
struct sockaddr
由于底层有不同的网络协议,所以它们的地址格式并不相同,所以通常使用struct sockaddr* 作为参数,然后根据前16位地址类型来确定协议内容。
参数 socklen_t address_len, 结构体sockaddr的长度。
typedef unsigned int socklen_t
返回值: 如果绑定成功则返回0, 失败则返回-1.
代码示例
int socket_fd = socket(AF_INET, SOCK_STREAN, 0);
if (socket_fd == -1)
{
exit(1);
}
struct sockaddr_in Server;
bzero(&Server, 0);
Server.sin_family = AF_INET;
Server.sin_addr.s_addr = inet_addr(_ip.c_str()); //?
Server.sin_port = htons(_port); //?
int n = bind( socket_fd, (const struct sockaddr *)&Server, sizeof(Server));
if (n != 0)
{
exit(2);
}
3. int listen(int socket, int backlog);
作用:将参数socket套接字用于监听,使其处于listen状态。
参数 backlog 这里暂时不讲,将其设为10左右即可。
返回值:成功则为0,失败则为-1。
代码示例
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
logMessage(FATAL, "socket create failed...");
exit(1);
}
logMessage(DEBUG, "socket create succeess...");
_listensock = sock;
// bind套接字
struct sockaddr_in local;
memset(&local, 0, sizeof local);
local.sin_family = AF_INET;
inet_aton(_server_ip.c_str(), &local.sin_addr);
local.sin_port = htons(_server_port);
if (bind(_listensock, (const sockaddr *)&local, (socklen_t)sizeof(local)) == -1)
{
logMessage(FATAL, "bind failed..., error:%s", strerror(errno));
exit(2);
}
logMessage(DEBUG, "bind succeess...");
// listen begin
if (listen(_listensock, backlog) < 0)
{
logMessage(FATAL, "listen failed...");
exit(3);
}
4. int accept(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);
作用:处于listen状态的网络套接字将持续监听是否有其他网络套接字对本套接字进行链接,该接口函数会阻塞,直到有套接字进行链接,链接成功后将返回一个文件描述符。
参数socket:处于listen状态的网络套接字。
参数struct sockaddr *restrict address: 输出型函数,用于保存远端主机的套接字信息。
参数socklen_t *restrict address_len: 输出型参数,用于保存远端主机的套接字的长度。
返回值:成功链接返回一个文件描述符,可对它进行读写操作,失败则返回-1。
5.int connect(int socket, const struct sockaddr *address,socklen_t address_len);
对目标网络套接字发送连接请求,一般适用于客户端去连接服务器。
参数 socket: 本地申请的套接字。
参数const struct sockaddr *address:目标网络套接字的struct sockaddr。
参数socklen_t address_len: 目标网络套接字的struct sockaddr的长度。
telnet通信
telnet是linux中可安装的程序,它是一种基于Tcp协议通信的程序,我们可以使用它来对我们的服务器进行测试。
终端输入该命令
telnet ip[xxx.xxx.xxx.xxx] port[1024+]
如果出现找不到该命令,则是因为你的linux主机没有安装telnet
输入
sudo yum install telnet
安装telnet
使用示例
telnet 127.0.0.1 8888
服务器代码
1. 单进程版本
// version 1 单进程单线程版
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8888;
const int backlog = 10;
std::string messageHandle(const std::string &ip, uint16_t port, const std::string &message)
{
time_t now = time(nullptr);
struct tm *lt = localtime(&now);
std::cout << lt->tm_hour << ":" << lt->tm_min << "[" << ip << ":" << port << "]: "
<< message << std::endl;
return message;
}
class TcpServer
{
public:
TcpServer(uint16_t port = default_port, std::string ip = default_ip)
: _listensock(-1), _server_ip(ip), _server_port(port)
{
}
void Init()
{
// 申请套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
logMessage(FATAL, "socket create failed...");
exit(1);
}
logMessage(DEBUG, "socket create succeess...");
_listensock = sock;
// bind套接字
struct sockaddr_in local;
memset(&local, 0, sizeof local);
local.sin_family = AF_INET;
inet_aton(_server_ip.c_str(), &local.sin_addr);
local.sin_port = htons(_server_port);
if (bind(_listensock, (const sockaddr *)&local, (socklen_t)sizeof(local)) == -1)
{
logMessage(FATAL, "bind failed..., error:%s", strerror(errno));
exit(2);
}
logMessage(DEBUG, "bind succeess...");
// listen begin
if (listen(_listensock, backlog) < 0)
{
logMessage(FATAL, "listen failed...");
exit(3);
}
logMessage(DEBUG, "listen succeess...");
}
void run()
{
struct sockaddr_in client;
socklen_t len;
while (true)
{
memset(&client, 0, sizeof client);
int socketfd = accept(_listensock, (struct sockaddr *)&client, &len);
if (socketfd < 0)
{
logMessage(WARNING, "accept failed...");
continue;
}
logMessage(DEBUG, "accept success..., and get a link, socketfd: %d", socketfd);
service(socketfd, client);
}
}
void service(const int socketfd, const struct sockaddr_in client)
{
uint16_t client_port = ntohs(client.sin_port);
char ip_buffer[32];
inet_ntop(AF_INET, (const void *)&client.sin_addr, ip_buffer, sizeof client);
std::string cilent_ip = ip_buffer;
// 开始接收消息
char buffer[1024];
while (true)
{
memset(buffer, 0, sizeof buffer);
int n = read(socketfd, buffer, sizeof buffer - 1);
if (n == 0)
{
logMessage(WARNING, "socketfd[%d] closed...", socketfd);
break;
}
else if (n < 0)
{
logMessage(WARNING, "read erro, socketfd[%d]...");
break;
}
if (buffer[n - 1] == '\n')
{
// 使用telnet通信
//std::cout << "发现\\n" << std::endl;
buffer[n - 2] = 0;
}
else
{
// 使用客户端通信
buffer[n] = 0;
}
std::string info = messageHandle(cilent_ip, client_port, buffer);
write(socketfd, info.c_str(), info.size());
}
close(socketfd);
logMessage(DEBUG, "socketfd: %d closed...", socketfd);
}
private:
int _listensock;
std::string _server_ip;
uint16_t _server_port;
};
该版本缺陷很明显,没办法同时处理多个客户端的数据请求。
2.多进程版本
// version 2 多进程版
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
#include <wait.h>
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8888;
const int backlog = 10;
std::string messageHandle(const std::string &ip, uint16_t port, const std::string &message)
{
time_t now = time(nullptr);
struct tm *lt = localtime(&now);
std::cout << lt->tm_hour << ":" << lt->tm_min << "[" << ip << ":" << port << "]: "
<< message << std::endl;
return message;
}
class TcpServer
{
public:
TcpServer(uint16_t port = default_port, std::string ip = default_ip)
: _listensock(-1), _server_ip(ip), _server_port(port)
{
}
void Init()
{
// 申请套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
logMessage(FATAL, "socket create failed...");
exit(1);
}
logMessage(DEBUG, "socket create succeess...");
_listensock = sock;
// bind套接字
struct sockaddr_in local;
memset(&local, 0, sizeof local);
local.sin_family = AF_INET;
inet_aton(_server_ip.c_str(), &local.sin_addr);
local.sin_port = htons(_server_port);
if (bind(_listensock, (const sockaddr *)&local, (socklen_t)sizeof(local)) == -1)
{
logMessage(FATAL, "bind failed..., error:%s", strerror(errno));
exit(2);
}
logMessage(DEBUG, "bind succeess...");
// listen begin
if (listen(_listensock, backlog) < 0)
{
logMessage(FATAL, "listen failed...");
exit(3);
}
logMessage(DEBUG, "listen succeess...");
}
void run()
{
struct sockaddr_in client;
socklen_t len;
while (true)
{
memset(&client, 0, sizeof client);
int socketfd = accept(_listensock, (struct sockaddr *)&client, &len);
if (socketfd < 0)
{
logMessage(WARNING, "accept failed...");
continue;
}
logMessage(DEBUG, "accept success..., and get a link, socketfd: %d", socketfd);
int pid = fork();
if (pid == 0)
{
if (fork())
{
// 子进程退出 这样我们的父进程就不需要等service函数执行完才waitpid
exit(0);
}
// 孙子进程
close(_listensock);
service(socketfd, client);
exit(0);
}
// 父进程
close(socketfd);
waitpid(pid, nullptr, 0);
}
}
void service(const int socketfd, const struct sockaddr_in client)
{
uint16_t client_port = ntohs(client.sin_port);
char ip_buffer[32];
inet_ntop(AF_INET, (const void *)&client.sin_addr, ip_buffer, sizeof client);
std::string cilent_ip = ip_buffer;
// 开始接收消息
char buffer[1024];
while (true)
{
memset(buffer, 0, sizeof buffer);
int n = read(socketfd, buffer, sizeof buffer - 1);
if (n == 0)
{
logMessage(WARNING, "socketfd[%d] closed...", socketfd);
break;
}
else if (n < 0)
{
logMessage(WARNING, "read erro, socketfd[%d]...");
break;
}
if (buffer[n - 1] == '\n')
{
// 使用telnet通信
// std::cout << "发现\\n" << std::endl;
buffer[n - 2] = 0;
}
else
{
// 使用客户端通信
buffer[n] = 0;
}
std::string info = messageHandle(cilent_ip, client_port, buffer);
write(socketfd, info.c_str(), info.size());
}
close(socketfd);
logMessage(DEBUG, "socketfd: %d closed...", socketfd);
}
private:
int _listensock;
std::string _server_ip;
uint16_t _server_port;
};
缺陷是多进程需要维护的系统资源较多,连接的客户端稍微多点就不行了,简而言之就是多进程占用太大。
3.多线程版本
// version 3 多线程版
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8888;
const int backlog = 10;
std::string messageHandle(const std::string &ip, uint16_t port, const std::string &message)
{
time_t now = time(nullptr);
struct tm *lt = localtime(&now);
std::cout << lt->tm_hour << ":" << lt->tm_min << "[" << ip << ":" << port << "]: "
<< message << std::endl;
return message;
}
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, struct sockaddr_in client)
: _socketfd(fd), _client(client)
{
}
int _socketfd;
struct sockaddr_in _client;
};
class TcpServer
{
public:
TcpServer(uint16_t port = default_port, std::string ip = default_ip)
: _listensock(-1), _server_ip(ip), _server_port(port)
{
}
void Init()
{
// 申请套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
logMessage(FATAL, "socket create failed...");
exit(1);
}
logMessage(DEBUG, "socket create succeess...");
_listensock = sock;
// bind套接字
struct sockaddr_in local;
memset(&local, 0, sizeof local);
local.sin_family = AF_INET;
inet_aton(_server_ip.c_str(), &local.sin_addr);
local.sin_port = htons(_server_port);
if (bind(_listensock, (const sockaddr *)&local, (socklen_t)sizeof(local)) == -1)
{
logMessage(FATAL, "bind failed..., error:%s", strerror(errno));
exit(2);
}
logMessage(DEBUG, "bind succeess...");
// listen begin
if (listen(_listensock, backlog) < 0)
{
logMessage(FATAL, "listen failed...");
exit(3);
}
logMessage(DEBUG, "listen succeess...");
}
void run()
{
struct sockaddr_in client;
socklen_t len;
while (true)
{
memset(&client, 0, sizeof client);
int socketfd = accept(_listensock, (struct sockaddr *)&client, &len);
if (socketfd < 0)
{
logMessage(WARNING, "accept failed...");
continue;
}
logMessage(DEBUG, "accept success..., and get a link, socketfd: %d", socketfd);
ThreadData *td = new ThreadData(socketfd, client);
pthread_t tid;
pthread_create(&tid, nullptr, service, (void *)td);
}
}
static void *service(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData *>(args);
uint16_t client_port = ntohs(td->_client.sin_port);
char ip_buffer[32];
inet_ntop(AF_INET, (const void *)&(td->_client.sin_addr), ip_buffer, sizeof(td->_client));
std::string cilent_ip = ip_buffer;
// 开始接收消息
char buffer[1024];
while (true)
{
memset(buffer, 0, sizeof buffer);
int n = read(td->_socketfd, buffer, sizeof buffer - 1);
if (n == 0)
{
logMessage(WARNING, "socketfd[%d] closed...", td->_socketfd);
break;
}
else if (n < 0)
{
logMessage(WARNING, "read erro, socketfd[%d]...");
break;
}
if (buffer[n - 1] == '\n')
{
// 使用telnet通信
// std::cout << "发现\\n" << std::endl;
buffer[n - 2] = 0;
}
else
{
// 使用客户端通信
buffer[n] = 0;
}
std::string info = messageHandle(cilent_ip, client_port, buffer);
write(td->_socketfd, info.c_str(), info.size());
}
delete td;
return nullptr;
}
private:
int _listensock;
std::string _server_ip;
uint16_t _server_port;
};
相对多进程版本,系统资源维护成本没那么大,也算一种不错的方案。
4. 线程池版本(附加字典功能)
Main.cc
#include <iostream>
#include "TcpServer.hpp"
#include "threadPool.hpp"
#include "Task.hpp"
void Usage(std::string proc)
{
std::cout << "\n\rUsage: " << proc << " port[8888-9000]\n"
<< std::endl;
}
//多线程版Main
int main(int argc, char* argv[])
{
if(argc != 2)
{
Usage("./TcpServer");
return 1;
}
int port = atoi(argv[1]);
TcpServer ts(port);
ts.Init();
ts.run();
return 0;
}
TcpServer.cpp
//TcpServer.cpp
// version 4 线程池版
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include "threadPool.hpp"
#include "Task.hpp"
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8888;
const int backlog = 10;
std::string messageHandle(const std::string &ip, uint16_t port, const std::string &message)
{
time_t now = time(nullptr);
struct tm *lt = localtime(&now);
std::cout << lt->tm_hour << ":" << lt->tm_min << "[" << ip << ":" << port << "]: "
<< message << std::endl;
return message;
}
class TcpServer;
class TcpServer
{
public:
TcpServer(uint16_t port = default_port, std::string ip = default_ip)
: _listensock(-1), _server_ip(ip), _server_port(port)
{
}
void Init()
{
// 申请套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
logMessage(FATAL, "socket create failed...");
exit(1);
}
logMessage(DEBUG, "socket create succeess...");
_listensock = sock;
// bind套接字
struct sockaddr_in local;
memset(&local, 0, sizeof local);
local.sin_family = AF_INET;
inet_aton(_server_ip.c_str(), &local.sin_addr);
local.sin_port = htons(_server_port);
if (bind(_listensock, (const sockaddr *)&local, (socklen_t)sizeof(local)) == -1)
{
logMessage(FATAL, "bind failed..., error:%s", strerror(errno));
exit(2);
}
logMessage(DEBUG, "bind succeess...");
// listen begin
if (listen(_listensock, backlog) < 0)
{
logMessage(FATAL, "listen failed...");
exit(3);
}
logMessage(DEBUG, "listen succeess...");
}
void run()
{
struct sockaddr_in client;
socklen_t len;
ThreadPool<Task>::GetInstance()->Start();
while (true)
{
memset(&client, 0, sizeof client);
int socketfd = accept(_listensock, (struct sockaddr *)&client, &len);
if (socketfd < 0)
{
logMessage(WARNING, "accept failed...");
continue;
}
logMessage(NORMAL, "accept success..., and get a link, socketfd: %d", socketfd);
ThreadPool<Task> *threadpool = ThreadPool<Task>::GetInstance();
threadpool->Push(Task(socketfd, client));
}
}
private:
int _listensock;
std::string _server_ip;
uint16_t _server_port;
};
ThreadPool.hpp
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
struct ThreadInfo
{
pthread_t tid;
std::string name;
};
static const int defalutnum = 10;
template <class T>
class ThreadPool
{
public:
void Lock()
{
pthread_mutex_lock(&mutex_);
}
void Unlock()
{
pthread_mutex_unlock(&mutex_);
}
void Wakeup()
{
pthread_cond_signal(&cond_);
}
void ThreadSleep()
{
pthread_cond_wait(&cond_, &mutex_);
}
bool IsQueueEmpty()
{
return tasks_.empty();
}
std::string GetThreadName(pthread_t tid)
{
for (const auto &ti : threads_)
{
if (ti.tid == tid)
return ti.name;
}
return "None";
}
public:
static void *HandlerTask(void *args)
{
ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
std::string name = tp->GetThreadName(pthread_self());
while (true)
{
tp->Lock();
while (tp->IsQueueEmpty())
{
tp->ThreadSleep();
}
T t = tp->Pop();
tp->Unlock();
t();
}
}
void Start()
{
int num = threads_.size();
for (int i = 0; i < num; i++)
{
threads_[i].name = "thread-" + std::to_string(i + 1);
pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);
}
}
T Pop()
{
T t = tasks_.front();
tasks_.pop();
return t;
}
void Push(const T &t)
{
Lock();
tasks_.push(t);
Wakeup();
Unlock();
}
static ThreadPool<T> *GetInstance()
{
if (nullptr == tp_) // ???
{
pthread_mutex_lock(&lock_);
if (nullptr == tp_)
{
// std::cout << "log: singleton create done first!" << std::endl;
tp_ = new ThreadPool<T>();
}
pthread_mutex_unlock(&lock_);
}
return tp_;
}
private:
ThreadPool(int num = defalutnum) : threads_(num)
{
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
~ThreadPool()
{
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
ThreadPool(const ThreadPool<T> &) = delete;
const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:
std::vector<ThreadInfo> threads_;
std::queue<T> tasks_;
pthread_mutex_t mutex_;
pthread_cond_t cond_;
static ThreadPool<T> *tp_;
static pthread_mutex_t lock_;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
Task.hpp
#pragma once
#include <functional>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "dictionary.hpp"
Dictionary dictionary;
class Task
{
public:
Task()
{
}
Task(int socketfd, const struct sockaddr_in client)
: _socketfd(socketfd), _client(client)
{
}
void operator()()
{
char key_word[64];
while (true)
{
memset(key_word, 0, sizeof key_word);
int n = read(_socketfd, key_word, sizeof key_word - 1);
if (n == 0)
{
logMessage(NORMAL, "Connection closed by foreign host, socketfd[%d] closed...", _socketfd);
break;
}
else if (n < 0)
{
logMessage(WARNING, "read erro, socketfd[%d]...");
break;
}
if (key_word[n - 1] == '\n')
{
// 使用telnet通信
logMessage(DEBUG, "find \\n, telnet connection...");
key_word[n - 2] = 0;
}
else
{
// 使用客户端通信
key_word[n] = 0;
}
std::string value_word = dictionary.translate(key_word);
write(_socketfd, value_word.c_str(), value_word.size());
}
close(_socketfd);
logMessage(DEBUG, "socketfd[%d] closed...", _socketfd);
}
private:
int _socketfd;
struct sockaddr_in _client;
};
log.hpp
#pragma once
#include <iostream>
#include <stdarg.h>
#include <string>
#include <time.h>
const char *levels[] = {
"NORMAL",
"WARNING",
"ERROR",
"FATAL",
"DEBUG"};
#define NORMAL 0
#define WARNING 1
#define ERROR 2
#define FATAL 3
#define DEBUG 4
void logMessage(int level, const char *format, ...)
{
#ifndef debug
if (level == DEBUG)
{
return;
}
#endif
char stdBuffer[1024];
time_t now = time(nullptr);
struct tm *lt = localtime(&now);
snprintf(stdBuffer, sizeof stdBuffer, "[%s][%d:%d]: ", levels[level], lt->tm_hour, lt->tm_min);
char logBuffer[1024];
va_list va;
va_start(va, format);
vsnprintf(logBuffer, sizeof logBuffer, format, va);
printf("%s%s\n", stdBuffer, logBuffer);
}
dictionary.hpp
#pragma once
#include <unordered_map>
#include <string>
#include <iostream>
#include <fstream>
const char separator = ':';
class Dictionary
{
public:
Dictionary()
{
Init();
}
bool split(const std::string &dword, std::string &part1, std::string &part2)
{
int pos = dword.find(separator, 0);
if (pos == std::string::npos)
{
return false;
}
part1 = dword.substr(0, pos);
part2 = dword.substr(pos + 1);
}
void Init()
{
std::ifstream in("dictionary.txt");
std::string dword;
while (std::getline(in, dword))
{
std::string part1;
std::string part2;
if (split(dword, part1, part2))
_dic[part1] = part2;
}
in.close();
}
std::string translate(std::string key)
{
auto it = _dic.find(key);
if (it == _dic.end())
{
return "word unknow";
}
return it->second;
}
private:
std::unordered_map<std::string, std::string> _dic;
};
dictionary.txt 字典文本(简易)
apple:苹果...
banana:香蕉...
red:红色...
yellow:黄色...
the: 这
be: 是
to: 朝向/给/对
and: 和
I: 我
in: 在...里
that: 那个
have: 有
will: 将
for: 为了
but: 但是
as: 像...一样
what: 什么
so: 因此
he: 他
her: 她
his: 他的
they: 他们
we: 我们
their: 他们的
his: 它的
with: 和...一起
she: 她
he: 他
it: 它
客户端代码
客户端代码仅此一套 且可适配服务器四个版本
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "log.hpp"
#include <netinet/in.h>
#include <string.h>
// telnet server_ip server_port
void Usage(const std::string &proc)
{
std::cout << "\n\rUsage: " << proc << " ip[xxx.xxx.xxx.xxx] port[8888-9000]\n"
<< std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage("./TcpClient");
exit(1);
}
// 申请socket
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd < 0)
{
logMessage(FATAL, "socket create failed...");
exit(2);
}
// 初始化结构体sockaddr
struct sockaddr_in server;
memset(&server, 0, sizeof server);
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
if (connect(socketfd, (const sockaddr *)&server, sizeof server) < 0)
{
// connect失败
logMessage(FATAL, "connect create failed...");
exit(3);
}
// connect 成功
logMessage(DEBUG, "connect create seccess...");
// 开始发送和读取数据
std::string out_buffer;
char in_buffer[1024];
while (true)
{
memset(in_buffer, 0, sizeof in_buffer);
std::cout << "Send a Message@ ";
std::getline(std::cin, out_buffer);
write(socketfd, out_buffer.c_str(), out_buffer.size());
int n = read(socketfd, in_buffer, sizeof in_buffer - 1);
if (n > 0)
{
in_buffer[n] = 0;
std::cout << "Server echo Message: " << in_buffer << std::endl;
}
else if (n == 0)
{
logMessage(WARNING, "Connection closed by foreign host, socketfd[%d] closed...", socketfd);
break;
}
else if (n < 0)
{
logMessage(WARNING, "read erro, socketfd[%d]...");
break;
}
}
close(socketfd);
return 0;
}