一、TCP_socket的API接口详解
下面是程序中用到的socket API,且这些函数都在sys/socket.h头文件中
1.1socket()


2、bind()

3、listen()

4、accept()

5、connect()

注意:客户端会在发起connect的时候,进行随机端口绑定。
二、单进程版的TCP服务端和客户端的连接以及实现
2.1TCP服务端
tcpServer.hpp文件:
cpp
#pragma once
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#include "Log.hpp"
log lg;
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; // 这个数字一般不要设置的太大
#define BUFFER_SIZE 4096
enum
{
usage_error = 1,
socket_error,
bind_error,
listen_error
};
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip)
: _listen_sockfd(defaultfd)
, _port(port)
, _ip(ip)
{
}
void InitServer()
{
_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listen_sockfd < 0)
{
// 创建套接字失败
lg.logmessage(Fatal, "create listen sockfd fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(socket_error);
}
else
{
// 创建成功
lg.logmessage(Info, "create listen socket success, sockfd:%d", _listen_sockfd);
// 填充网络信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
inet_aton(_ip.c_str(), &(local.sin_addr));
// 或者把它写成
// local.sin_addr.s_addr = INADDR_ANY;
// 绑定
int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
lg.logmessage(Fatal, "bind fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(bind_error);
}
else
{
// 绑定套接字成功
lg.logmessage(Info, "bind socket success, _listen_sockfd:%d", _listen_sockfd);
}
// tcp是面向连接的,服务器一般是比较被动的,也就是服务器一直处于一种一直在等待连接到来的状态
if (listen(_listen_sockfd, backlog) < 0)
{
lg.logmessage(Fatal, "listen fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(listen_error);
}
else
{
// 监听成功
lg.logmessage(Info, "listen socket success, _listen_sockfd:%d", _listen_sockfd);
}
}
}
void RunServer()
{
lg.logmessage(Info, "tcpServer id running......");
while (true)
{
// 1、获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg.logmessage(Warning, "accept fail, errno: %d, errmessage: %s", errno, strerror(errno));
continue;
}
else
{
// 获取客户端的ip地址和端口号
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2、根据新链接来进行通信
lg.logmessage(Info, "get a new link..., sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip, clientport);
// 通信场景一:发什么返回什么
Server(sockfd, clientip, clientport);
close(sockfd);
}
}
}
void Server(const int sockfd, const std::string &clientip, const uint16_t &clientport)
{
char buffer[BUFFER_SIZE];
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer));
if (n < 0)
{
lg.logmessage(Warning, "read error, sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip.c_str(), clientport);
break;
}
else if (n == 0)
{
lg.logmessage(Info, "%s:%d quit,server close, sockfd:%d", clientip.c_str(), clientport, sockfd);
break;
}
else
{
buffer[n] = '\0';
std::cout << "client says#:" << buffer << std::endl;
std::string echo_string = "tcpServer says#:";
echo_string += buffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
}
}
~TcpServer()
{
}
private:
int _listen_sockfd;
uint16_t _port;
std::string _ip;
};
Log.hpp文件:
cpp
#pragma once
#include <iostream>
#include <stdarg.h>
#include <time.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include <fcntl.h>
#include<string.h>
#define SIZE 1024
// 设置日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
#define Screen 1
#define Onefile 2
#define Classfile 3
#define logFile "log.txt"
class log
{
public:
log()
{
PrintMethod = Screen;
}
void Enable(int method)
{
PrintMethod = method;
}
std::string levelToString(int level)
{
switch (level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warning";
case Error:
return "Error";
default:
return "None";
}
}
void logmessage(int level, const char *format, ...) // 后面的省略号表示可变参数
{
char leftbuffer[SIZE];
time_t t = time(nullptr);
struct tm *ctime = localtime(&t);
snprintf(leftbuffer, sizeof(leftbuffer), "[%s],[%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
char rightbuffer[SIZE];
va_list s;
va_start(s, format);
vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
va_end(s);
char logtxt[SIZE * 2];
snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);
// printf("%d-%d-%d %d:%d:%d\n",ctime->tm_year + 1900, ctime->tm_mon, ctime->tm_mday, ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
//printf("%s", logtxt);
PrintLog(level,logtxt);
// 格式:默认部分+自定义部分(可变参数部分)
}
void PrintLog(int level, const std::string& logtxt)
{
switch(PrintMethod)
{
case Screen:
std::cout << logtxt << std::endl;
break;
case Onefile:
printOneFile(logFile, logtxt);
break;
case Classfile:
printClassFile(level, logtxt);
break;
default:
break;
}
}
void printOneFile(const std::string &logname, const std::string &logtxt)
{
int fd = open(logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
if(fd < 0)
{
return ;
}
write(fd,logtxt.c_str(),logtxt.size());
close(fd);
}
void printClassFile(int level, const std::string &logtxt)
{
std::string filename = logFile;
filename += ".";
filename += levelToString(level);
}
~log()
{
}
private:
int PrintMethod;
};
int sum(int n, ...)
{
va_list s;
va_start(s, n);
int sum = 0;
while (n)
{
sum += va_arg(s, int);
n--;
}
va_end(s);
return sum;
}
main.cc文件:
cpp
#include "tcpServer.hpp"
void Usage(std::string process)
{
std::cout << "\n\rUsage:" << process << " port[1024+]" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(usage_error);
}
uint16_t port = std::stoi(argv[1]);
std::unique_ptr<TcpServer> ts(new TcpServer(port));
ts->InitServer();
ts->RunServer();
return 0;
}
2.2TCP客户端
tcpClient.cc文件:
cpp
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#define IN_BUFFER_SIZE 4096
void Usage(const std::string &proc)
{
std::cout << "\n\rUsage:" << proc << "serverip serverport\n"
<< std::endl;
}
//./tcpClient serverip serverport
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
return 1;
}
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));
// tcp客户端也要进行绑定,但是也是系统进行绑定,绑定随机端口
// 客户端在发起connect的时候,进行随机绑定
int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if (n < 0)
{
// 连接失败
std::cerr << "connet error..." << std::endl;
return 2;
}
std::string message;
// 链接成功
while (true)
{
std::cout << "please Enter@:";
std::getline(std::cin, message);
write(sockfd, message.c_str(), message.size());
char in_buffer[IN_BUFFER_SIZE];
int n = read(sockfd, in_buffer, sizeof(in_buffer));
if (n > 0)
{
in_buffer[n] = '\0';
std::cout << in_buffer << std::endl;
}
}
close(sockfd);
return 0;
}
2.3Makefile文件
bash
.PHONY:all
all:tcpServer tcpClient
tcpServer:main.cc
g++ -o $@ $^ -std=c++11
tcpClient:tcpClient.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -rf tcpClient tcpServer
2.4运行结果以及总结:

三、多进程版本的TCP服务端和客户端的连接以及实现
注意:多进程版本的只需要在服务端的代码上进行更改,客户端的代码就和前面的一样
TCP服务端:
tcpServer.hpp文件:
cpp
#pragma once
#include<unistd.h>
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#include<sys/wait.h>
#include<signal.h>
#include "Log.hpp"
log lg;
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; // 这个数字一般不要设置的太大
#define BUFFER_SIZE 4096
enum
{
usage_error = 1,
socket_error,
bind_error,
listen_error
};
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip)
: _listen_sockfd(defaultfd)
, _port(port)
, _ip(ip)
{
}
void InitServer()
{
_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listen_sockfd < 0)
{
// 创建套接字失败
lg.logmessage(Fatal, "create listen sockfd fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(socket_error);
}
else
{
// 创建成功
lg.logmessage(Info, "create listen socket success, sockfd:%d", _listen_sockfd);
// 填充网络信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
inet_aton(_ip.c_str(), &(local.sin_addr));
// 或者把它写成
// local.sin_addr.s_addr = INADDR_ANY;
// 绑定
int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
lg.logmessage(Fatal, "bind fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(bind_error);
}
else
{
// 绑定套接字成功
lg.logmessage(Info, "bind socket success, _listen_sockfd:%d", _listen_sockfd);
}
// tcp是面向连接的,服务器一般是比较被动的,也就是服务器一直处于一种一直在等待连接到来的状态
if (listen(_listen_sockfd, backlog) < 0)
{
lg.logmessage(Fatal, "listen fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(listen_error);
}
else
{
// 监听成功
lg.logmessage(Info, "listen socket success, _listen_sockfd:%d", _listen_sockfd);
}
}
}
void RunServer()
{
lg.logmessage(Info, "tcpServer id running......");
while (true)
{
// 1、获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg.logmessage(Warning, "accept fail, errno: %d, errmessage: %s", errno, strerror(errno));
continue;
}
else
{
//方法二、不用等待
//signal(SIGCHLD, SIG_IGN);
// 获取客户端的ip地址和端口号
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2、根据新链接来进行通信
lg.logmessage(Info, "get a new link..., sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip, clientport);
// 通信场景二:发什么返回什么(多进程版)
pid_t id = fork();
if(id == 0)
{
//子进程
close(_listen_sockfd);
//实现并发执行的方案一,让该子进程的孙子进程去执行代码
if(fork() > 0)
{
exit(0);
}
//孙子进程执行---任务执行完,孙子进程会被系统领养
Server(sockfd, clientip, clientport);
close(sockfd);
exit(0);
}
//sockfd已经被子进程拿到了,所以我们需要关闭父进程的sockfd,不然父进程的文件描述符会越用越少
close(sockfd);
//父进程
pid_t rid = waitpid(id, nullptr, 0);
}
}
}
void Server(const int sockfd, const std::string &clientip, const uint16_t &clientport)
{
char buffer[BUFFER_SIZE];
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer));
if (n < 0)
{
lg.logmessage(Warning, "read error, sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip.c_str(), clientport);
break;
}
else if (n == 0)
{
lg.logmessage(Info, "%s:%d quit,server close, sockfd:%d", clientip.c_str(), clientport, sockfd);
break;
}
else
{
buffer[n] = '\0';
std::cout << "client says#:" << buffer << std::endl;
std::string echo_string = "tcpServer says#:";
echo_string += buffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
}
}
~TcpServer()
{
}
private:
int _listen_sockfd;
uint16_t _port;
std::string _ip;
};
运行结果以及总结:

四、多线程版本的TCP服务端和客户端的连接以及实现
注意:多线程版也只需要更改服务端的代码即可:
TCP服务端:
tcpServer.hpp文件:
cpp
#pragma once
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include<pthread.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#include "Log.hpp"
log lg;
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; // 这个数字一般不要设置的太大
#define BUFFER_SIZE 4096
enum
{
usage_error = 1,
socket_error,
bind_error,
listen_error
};
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
:sockfd(fd)
,clientip(ip)
,clientport(port)
,_ts(t)
{
}
public:
int sockfd;
std::string clientip;
uint16_t clientport;
TcpServer *_ts;
};
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip)
: _listen_sockfd(defaultfd)
, _port(port)
, _ip(ip)
{
}
void InitServer()
{
_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listen_sockfd < 0)
{
// 创建套接字失败
lg.logmessage(Fatal, "create listen sockfd fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(socket_error);
}
else
{
// 创建成功
lg.logmessage(Info, "create listen socket success, sockfd:%d", _listen_sockfd);
// 填充网络信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
inet_aton(_ip.c_str(), &(local.sin_addr));
// 或者把它写成
// local.sin_addr.s_addr = INADDR_ANY;
// 绑定
int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
lg.logmessage(Fatal, "bind fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(bind_error);
}
else
{
// 绑定套接字成功
lg.logmessage(Info, "bind socket success, _listen_sockfd:%d", _listen_sockfd);
}
// tcp是面向连接的,服务器一般是比较被动的,也就是服务器一直处于一种一直在等待连接到来的状态
if (listen(_listen_sockfd, backlog) < 0)
{
lg.logmessage(Fatal, "listen fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(listen_error);
}
else
{
// 监听成功
lg.logmessage(Info, "listen socket success, _listen_sockfd:%d", _listen_sockfd);
}
}
}
static void *Routinue(void *args)
{
//线程分离
pthread_detach(pthread_self());
ThreadData * td = static_cast<ThreadData *>(args);
td->_ts->Server(td->sockfd, td->clientip, td->clientport);
delete td;
return nullptr;
}
void RunServer()
{
lg.logmessage(Info, "tcpServer id running......");
while (true)
{
// 1、获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg.logmessage(Warning, "accept fail, errno: %d, errmessage: %s", errno, strerror(errno));
continue;
}
else
{
// 获取客户端的ip地址和端口号
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2、根据新链接来进行通信
lg.logmessage(Info, "get a new link..., sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip, clientport);
// 通信场景三:发什么返回什么(多线程版本)
ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
pthread_t tid;
pthread_create(&tid, nullptr, Routinue, td);
}
}
}
void Server(const int sockfd, const std::string &clientip, const uint16_t &clientport)
{
char buffer[BUFFER_SIZE];
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer));
if (n < 0)
{
lg.logmessage(Warning, "read error, sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip.c_str(), clientport);
break;
}
else if (n == 0)
{
lg.logmessage(Info, "%s:%d quit,server close, sockfd:%d", clientip.c_str(), clientport, sockfd);
break;
}
else
{
buffer[n] = '\0';
std::cout << "client says#:" << buffer << std::endl;
std::string echo_string = "tcpServer says#:";
echo_string += buffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
}
}
~TcpServer()
{
}
private:
int _listen_sockfd;
uint16_t _port;
std::string _ip;
};
运行结果以及总结:

五、线程池版本的TCP服务端和客户端的连接以及实现
注意:进程池与前面一样,只需要加入进程池即可
TCP服务端:
(1)tcpServer.hpp文件:
cpp
#pragma once
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include<pthread.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#include "Log.hpp"
#include "threadpool.hpp"
#include "Task.hpp"
log lg;
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; // 这个数字一般不要设置的太大
#define BUFFER_SIZE 4096
enum
{
usage_error = 1,
socket_error,
bind_error,
listen_error
};
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
:sockfd(fd)
,clientip(ip)
,clientport(port)
,_ts(t)
{
}
public:
int sockfd;
std::string clientip;
uint16_t clientport;
TcpServer *_ts;
};
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip)
: _listen_sockfd(defaultfd)
, _port(port)
, _ip(ip)
{
}
void InitServer()
{
_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listen_sockfd < 0)
{
// 创建套接字失败
lg.logmessage(Fatal, "create listen sockfd fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(socket_error);
}
else
{
// 创建成功
lg.logmessage(Info, "create listen socket success, sockfd:%d", _listen_sockfd);
// 填充网络信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
inet_aton(_ip.c_str(), &(local.sin_addr));
// 或者把它写成
// local.sin_addr.s_addr = INADDR_ANY;
// 绑定
int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
lg.logmessage(Fatal, "bind fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(bind_error);
}
else
{
// 绑定套接字成功
lg.logmessage(Info, "bind socket success, _listen_sockfd:%d", _listen_sockfd);
}
// tcp是面向连接的,服务器一般是比较被动的,也就是服务器一直处于一种一直在等待连接到来的状态
if (listen(_listen_sockfd, backlog) < 0)
{
lg.logmessage(Fatal, "listen fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(listen_error);
}
else
{
// 监听成功
lg.logmessage(Info, "listen socket success, _listen_sockfd:%d", _listen_sockfd);
}
}
}
void RunServer()
{
//启动线程池
ThreadPool<Task>::GetInstance()->start();
lg.logmessage(Info, "tcpServer id running......");
while (true)
{
// 1、获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg.logmessage(Warning, "accept fail, errno: %d, errmessage: %s", errno, strerror(errno));
continue;
}
else
{
// 获取客户端的ip地址和端口号
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2、根据新链接来进行通信
lg.logmessage(Info, "get a new link..., sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip, clientport);
// 通信场景四:执行任务(线程池版本)
ThreadPool<Task>::GetInstance()->push(Task(sockfd, clientip, clientport));
}
}
}
~TcpServer()
{
}
private:
int _listen_sockfd;
uint16_t _port;
std::string _ip;
};
(2)进程池代码---threadpool.hpp文件:
cpp
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <time.h>
#include "Task.hpp"
struct threadInfo
{
pthread_t tid;
std::string name;
};
static const int defaultnum = 10;
template <typename T>
class ThreadPool
{
protected:
void lock()
{
pthread_mutex_lock(&_mutex);
}
void unlock()
{
pthread_mutex_unlock(&_mutex);
}
void ThreadWakeup()
{
pthread_cond_signal(&_cond);
}
void ThreadSleep()
{
pthread_cond_wait(&_cond, &_mutex);
}
bool QueueIsEmpty()
{
return _tasks.empty();
}
std::string GetThreadName(pthread_t tid)
{
for (const auto &ti : _threads)
{
if (ti.tid == tid)
{
return ti.name;
}
}
return "None";
}
public:
ThreadPool(int num = defaultnum)
: _threads(num)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_cond, nullptr);
}
static void *HandlerTask(void *args) // 使用static把该函数变成静态成员函数,而不再是类内成员函数,从而没有了this指针
{
// 因为加上了static,已经是全局的静态函数,而不再是类内的函数了,无法直接访问类内成员,所以要传this指针
ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
std::string name = tp->GetThreadName(pthread_self());
while (true)
{
tp->lock();
while (tp->QueueIsEmpty())
{
tp->ThreadSleep();
}
// 取出任务
T t = tp->pop();
tp->unlock();
t();
sleep(1);
}
}
void start()
{
for (int i = 0; i < _threads.size(); i++)
{
_threads[i].name = "thread-" + std::to_string(i);
pthread_create(&(_threads[i].tid), nullptr, HandlerTask, this);
}
}
T pop()
{
T t = _tasks.front();
_tasks.pop();
return t;
}
void push(const T &task)
{
lock();
_tasks.push(task);
// 唤醒休眠的进程
ThreadWakeup();
unlock();
}
~ThreadPool()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
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;
}
ThreadPool(const ThreadPool<T> &) = delete;
const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
protected:
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;
(3)任务代码Task.hpp文件:
cpp
#pragma once
#pragma once
#include <iostream>
#include <string>
#include "Log.hpp"
#define BUFFER_SIZE 4096
extern log lg;
class Task
{
public:
Task(int sockfd, const std::string &clientip, const uint16_t &clientport)
: _sockfd(sockfd), _clientip(clientip), _clientport(clientport)
{
}
Task()
{
}
void Run()
{
// 测试代码
char buffer[BUFFER_SIZE];
ssize_t n = read(_sockfd, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
std::cout << "client key# " << buffer << std::endl;
std::string echo_string = "tcpserver echo#";
echo_string += buffer;
n = write(_sockfd, echo_string.c_str(), echo_string.size());
if (n < 0)
{
lg.logmessage(Warning, "write error, errno : %d, errstring: %s", errno, strerror(errno));
}
}
else if (n == 0)
{
lg.logmessage(Info, "%s:%d quit, server close sockfd: %d", _clientip.c_str(), _clientport, _sockfd);
}
else
{
lg.logmessage(Warning, "read error, sockfd: %d, client ip: %s, client port: %d"
, _sockfd, _clientip.c_str(), _clientport);
}
close(_sockfd);
}
void operator()()
{
Run();
}
~Task()
{
}
private:
int _sockfd;
std::string _clientip;
uint16_t _clientport;
};
(4)运行结果以及总结

六、tcp_socket套接字应用之字典的实现(守护进程版本)
6.1客户端代码(tcpClient.cc文件):
注意:客户端代码实现了断联后重新与服务器连接等功能
cpp
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#define IN_BUFFER_SIZE 4096
void Usage(const std::string &proc)
{
std::cout << "\n\rUsage:" << proc << "serverip serverport\n"
<< std::endl;
}
//./tcpClient serverip serverport
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string serverip = argv[1];
uint16_t serverport = std::stoi(argv[2]);
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);
inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));
while (true)
{
// 创建重连模块
int cnt = 7;
int iscorrecting = false;
int sockfd = 0;
do
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
return 1;
}
// tcp客户端也要进行绑定,但是也是系统进行绑定,绑定随机端口
// 客户端在发起connect的时候,进行随机绑定
int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if (n < 0)
{
// 连接失败
cnt--;
std::cerr << "connet error...,正在尝试重新连接...[" << cnt << "/7]" << std::endl;
iscorrecting = true;
sleep(1);
}
else
{
break;
}
} while (cnt && iscorrecting);
// 五次重连失败后:
if (cnt == 0)
{
std::cerr << "user offline" << std::endl;
break;
}
// 链接成功
while (true)
{
std::string message;
std::cout << "please Enter@:";
std::getline(std::cin, message);
int n = write(sockfd, message.c_str(), message.size());
if (n < 0)
{
iscorrecting = true;
std::cerr << "write error..." << std::endl;
break;
}
char in_buffer[IN_BUFFER_SIZE];
n = read(sockfd, in_buffer, sizeof(in_buffer));
if (n > 0)
{
in_buffer[n] = '\0';
std::cout << in_buffer << std::endl;
}
else
{
break;
}
}
close(sockfd);
}
return 0;
}
6.2服务端代码:
tcpServer.hpp文件:
cpp
#pragma once
#include <stdio.h>
#include <iostream>
#include <memory>
#include <vector>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<signal.h>
#include <arpa/inet.h>
#include <string>
#include <arpa/inet.h>
#include<pthread.h>
#include <strings.h>
#include <functional>
#include <stdbool.h>
#include "Log.hpp"
#include "threadpool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"
log lg;
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; // 这个数字一般不要设置的太大
#define BUFFER_SIZE 4096
enum
{
usage_error = 1,
socket_error,
bind_error,
listen_error
};
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
:sockfd(fd)
,clientip(ip)
,clientport(port)
,_ts(t)
{
}
public:
int sockfd;
std::string clientip;
uint16_t clientport;
TcpServer *_ts;
};
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip)
: _listen_sockfd(defaultfd)
, _port(port)
, _ip(ip)
{
}
void InitServer()
{
_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_listen_sockfd < 0)
{
// 创建套接字失败
lg.logmessage(Fatal, "create listen sockfd fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(socket_error);
}
else
{
// 创建成功
lg.logmessage(Info, "create listen socket success, sockfd:%d", _listen_sockfd);
// 填充网络信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
inet_aton(_ip.c_str(), &(local.sin_addr));
// 或者把它写成
// local.sin_addr.s_addr = INADDR_ANY;
// 绑定
int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
lg.logmessage(Fatal, "bind fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(bind_error);
}
else
{
// 绑定套接字成功
lg.logmessage(Info, "bind socket success, _listen_sockfd:%d", _listen_sockfd);
}
// tcp是面向连接的,服务器一般是比较被动的,也就是服务器一直处于一种一直在等待连接到来的状态
if (listen(_listen_sockfd, backlog) < 0)
{
lg.logmessage(Fatal, "listen fail, errno: %d, errmessage: %s", errno, strerror(errno));
exit(listen_error);
}
else
{
// 监听成功
lg.logmessage(Info, "listen socket success, _listen_sockfd:%d", _listen_sockfd);
}
}
}
void RunServer()
{
//守护进程
Daemon();
//忽略sigpipe信号,防止突然一端断开后服务区终止
signal(SIGPIPE, SIG_IGN);
//启动线程池
ThreadPool<Task>::GetInstance()->start();
lg.logmessage(Info, "tcpServer id running......");
while (true)
{
// 1、获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg.logmessage(Warning, "accept fail, errno: %d, errmessage: %s", errno, strerror(errno));
continue;
}
else
{
// 获取客户端的ip地址和端口号
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2、根据新链接来进行通信
lg.logmessage(Info, "get a new link..., sockfd:%d, clientip:%s, clientport: %d"
, sockfd, clientip, clientport);
// 通信场景四:执行任务(线程池版本)
ThreadPool<Task>::GetInstance()->push(Task(sockfd, clientip, clientport));
}
}
}
~TcpServer()
{
}
private:
int _listen_sockfd;
uint16_t _port;
std::string _ip;
};
threadpool.hpp文件:
cpp
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <time.h>
#include "Task.hpp"
struct threadInfo
{
pthread_t tid;
std::string name;
};
static const int defaultnum = 10;
template <typename T>
class ThreadPool
{
protected:
void lock()
{
pthread_mutex_lock(&_mutex);
}
void unlock()
{
pthread_mutex_unlock(&_mutex);
}
void ThreadWakeup()
{
pthread_cond_signal(&_cond);
}
void ThreadSleep()
{
pthread_cond_wait(&_cond, &_mutex);
}
bool QueueIsEmpty()
{
return _tasks.empty();
}
std::string GetThreadName(pthread_t tid)
{
for (const auto &ti : _threads)
{
if (ti.tid == tid)
{
return ti.name;
}
}
return "None";
}
public:
ThreadPool(int num = defaultnum)
: _threads(num)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_cond, nullptr);
}
static void *HandlerTask(void *args) // 使用static把该函数变成静态成员函数,而不再是类内成员函数,从而没有了this指针
{
// 因为加上了static,已经是全局的静态函数,而不再是类内的函数了,无法直接访问类内成员,所以要传this指针
ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
std::string name = tp->GetThreadName(pthread_self());
while (true)
{
tp->lock();
while (tp->QueueIsEmpty())
{
tp->ThreadSleep();
}
// 取出任务
T t = tp->pop();
tp->unlock();
t();
sleep(1);
}
}
void start()
{
for (int i = 0; i < _threads.size(); i++)
{
_threads[i].name = "thread-" + std::to_string(i);
pthread_create(&(_threads[i].tid), nullptr, HandlerTask, this);
}
}
T pop()
{
T t = _tasks.front();
_tasks.pop();
return t;
}
void push(const T &task)
{
lock();
_tasks.push(task);
// 唤醒休眠的进程
ThreadWakeup();
unlock();
}
~ThreadPool()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
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;
}
ThreadPool(const ThreadPool<T> &) = delete;
const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
protected:
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文件:
cpp
#pragma once
#pragma once
#include <iostream>
#include <string>
#include<unordered_map>
#include "Log.hpp"
#include "Init.hpp"
#define BUFFER_SIZE 4096
extern log lg;
Init init;
class Task
{
public:
Task(int sockfd, const std::string &clientip, const uint16_t &clientport)
: _sockfd(sockfd), _clientip(clientip), _clientport(clientport)
{
}
Task()
{
}
void Run()
{
// 测试代码
char buffer[BUFFER_SIZE];
ssize_t n = read(_sockfd, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
std::cout << "client key# " << buffer << std::endl;
std::string echo_string = init.translation(buffer);
n = write(_sockfd, echo_string.c_str(), echo_string.size());
if (n < 0)
{
lg.logmessage(Warning, "write error, errno : %d, errstring: %s", errno, strerror(errno));
}
}
else if (n == 0)
{
lg.logmessage(Info, "%s:%d quit, server close sockfd: %d", _clientip.c_str(), _clientport, _sockfd);
}
else
{
lg.logmessage(Warning, "read error, sockfd: %d, client ip: %s, client port: %d"
, _sockfd, _clientip.c_str(), _clientport);
}
close(_sockfd);
}
void operator()()
{
Run();
}
~Task()
{
}
private:
int _sockfd;
std::string _clientip;
uint16_t _clientport;
};
Log.hpp文件:
cpp
#pragma once
#include <iostream>
#include <stdarg.h>
#include <time.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include <fcntl.h>
#include<string.h>
#define SIZE 1024
// 设置日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
#define Screen 1
#define Onefile 2
#define Classfile 3
#define logFile "log.txt"
class log
{
public:
log()
{
PrintMethod = Screen;
path = "./log/";
}
void Enable(int method)
{
PrintMethod = method;
}
std::string levelToString(int level)
{
switch (level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warning";
case Error:
return "Error";
default:
return "None";
}
}
void logmessage(int level, const char *format, ...) // 后面的省略号表示可变参数
{
char leftbuffer[SIZE];
time_t t = time(nullptr);
struct tm *ctime = localtime(&t);
snprintf(leftbuffer, sizeof(leftbuffer), "[%s],[%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
char rightbuffer[SIZE];
va_list s;
va_start(s, format);
vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
va_end(s);
char logtxt[SIZE * 2];
snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);
// printf("%d-%d-%d %d:%d:%d\n",ctime->tm_year + 1900, ctime->tm_mon, ctime->tm_mday, ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
//printf("%s", logtxt);
PrintLog(level,logtxt);
// 格式:默认部分+自定义部分(可变参数部分)
}
void PrintLog(int level, const std::string& logtxt)
{
switch(PrintMethod)
{
case Screen:
std::cout << logtxt << std::endl;
break;
case Onefile:
printOneFile(logFile, logtxt);
break;
case Classfile:
printClassFile(level, logtxt);
break;
default:
break;
}
}
void printOneFile(const std::string &logname, const std::string &logtxt)
{
std::string _logname = path + logname;
int fd = open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
if(fd < 0)
{
return ;
}
write(fd,logtxt.c_str(),logtxt.size());
close(fd);
}
void printClassFile(int level, const std::string &logtxt)
{
std::string filename = logFile;
filename += ".";
filename += levelToString(level);
printOneFile(filename, logtxt);
}
~log()
{
}
private:
int PrintMethod;
std::string path;
};
Init.hpp文件:
cpp
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include <stdbool.h>
#include "Log.hpp"
extern log lg;
const std::string dictname = "./dict.txt";
const std::string sep = ":";
static bool Split(std::string &s, std::string *English, std::string *Chinese)
{
auto pos = s.find(sep);
if (pos == std::string::npos)
{
return false;
}
else
{
*English = s.substr(0, pos);
*Chinese = s.substr(pos + 1);
}
return true;
}
class Init
{
public:
Init()
{
std::ifstream in(dictname);
if (!in.is_open())
{
lg.logmessage(Fatal, "dict file open error");
exit(1);
}
std::string line;
while (std::getline(in, line))
{
// 切割
std::string English;
std::string Chinese;
Split(line, &English, &Chinese);
dict.insert({English, Chinese});
}
in.close();
}
std::string translation(const std::string &key)
{
auto iter = dict.find(key);
if (iter == dict.end())
{
return "Unknow";
}
else
{
return iter->second;
}
}
private:
std::unordered_map<std::string, std::string> dict;
};
守护进程(Daemon.hpp文件):
cpp
#pragma once
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
// Linux系统提供的垃圾文件
const std::string nullfile = "/dev/null";
void Daemon(const std::string &cwd = " ")
{
// 忽略其他异常信号
signal(SIGCLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
// 进来是父进程,出去就是子进程了(让自己不要成为组长)
if (fork() > 0)
{
exit(0);
}
// 让自己成为新的会话
setsid();
// 更改守护进程的目录
if (!cwd.empty())
{
chdir(cwd.c_str());
}
// 关标准输入标准输出和标准错误全部重定向至/dev/null中
int fd = open(nullfile.c_str(), O_RDWR);
if (fd > 0)
{
dup2(fd, 1);
dup2(fd, 0);
dup2(fd, 3);
close(fd);
}
}
main.cc文件:
cpp
#include "tcpServer.hpp"
void Usage(std::string process)
{
std::cout << "\n\rUsage:" << process << " port[1024+]" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(usage_error);
}
uint16_t port = std::stoi(argv[1]);
// 开启日志功能
lg.Enable(Classfile);
std::unique_ptr<TcpServer> ts(new TcpServer(port));
ts->InitServer();
ts->RunServer();
return 0;
}
文本文件(dict.txt)文件:
bash
apple:苹果
love:爱
sort:排序
compare:比较
miss:想念
kiss:接吻
like:喜欢
mother:母亲
father:父亲
parent:父母
banana:香蕉
6.3运行结果以及总结:
