小编个人主页详情<—请点击 小编个人gitee代码仓库<—请点击 linux系统编程专栏<—请点击 linux网络编程专栏<—请点击 倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己! 
目录
-
- 前言
- 一、多进程版
-
- 逻辑一
-
- 测试
- 逻辑二
-
- 测试
- 二、多线程版
-
-
- 测试
-
- 三、源代码
-
- makefile
- TcpServer.hpp
- Main.cc
- TcpCilent.cc
- Log.hpp
- 总结
前言
【linux】网络套接字编程(四)TCP服务器与客户端的实现(单进程/单线程的TCP服务器),setsockopt,listen,accept,telnet,connect,inet_pton——书接上文 详情请点击<——,本文会在上文的基础上进行讲解,所以对上文不了解的读者友友请点击前方的蓝字链接进行学习 本文由小编为大家介绍——【linux】网络套接字编程(五)TCP服务器与客户端的实现——多进程版,多线程版
一、多进程版
逻辑一


void StartServer()
{
lg(Info, "tcpserver is running…");
for (;;)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);
if (sockfd < 0)
{
lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
continue;
}
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
lg(Info, "get a new link…, sockfd: %d, client ip: %s, client port: %d",
sockfd, clientip, clientport);
//version 1 单进程/单线程版
// Service(sockfd, clientip, clientport);
// close(sockfd);
//version 2 多进程版
pid_t id = fork();
if(id == 0)
{
//child
close(listensock_);
Service(sockfd, clientip, clientport);
close(sockfd);
exit(0);
}
close(sockfd);
pid_t rid = waitpid(id, nullptr, 0);
if(rid == id)
std::cout << "father wait success" << std::endl;
}
}
测试



逻辑二
void StartServer()
{
lg(Info, "tcpserver is running…");
for (;;)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);
if (sockfd < 0)
{
lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
continue;
}
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
lg(Info, "get a new link…, sockfd: %d, client ip: %s, client port: %d",
sockfd, clientip, clientport);
//version 1 单进程/单线程版
// Service(sockfd, clientip, clientport);
// close(sockfd);
//version 2 多进程版
pid_t id = fork();
if(id == 0)
{
//child
close(listensock_);
if(fork() > 0)
exit(0);
//grandchild
Service(sockfd, clientip, clientport);
close(sockfd);
exit(0);
}
close(sockfd);
pid_t rid = waitpid(id, nullptr, 0);
if(rid == id)
std::cout << "father wait success" << std::endl;
}
}
测试




ps axj | head -1 && ps axj | grep tcp | grep -v grep

二、多线程版
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, const std::string& ip, const uint16_t& p, TcpServer* t)
: sockfd(fd)
, clientip(ip)
, clientport(p)
, tsvr(t)
{}
public:
int sockfd;
std::string clientip;
uint16_t clientport;
TcpServer* tsvr;
};
static void* Routine(void* args)
{
pthread_detach(pthread_self());
ThreadData* td = static_cast<ThreadData*>(args);
td->tsvr->Service(td->sockfd, td->clientip, td->clientport);
close(td->sockfd);
delete td;
return nullptr;
}
void StartServer()
{
lg(Info, "tcpserver is running…");
for (;;)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);
if (sockfd < 0)
{
lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
continue;
}
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
lg(Info, "get a new link…, sockfd: %d, client ip: %s, client port: %d",
sockfd, clientip, clientport);
//version 1 单进程/单线程版
// Service(sockfd, clientip, clientport);
// close(sockfd);
//version 2 多进程版
// pid_t id = fork();
// if(id == 0)
// {
// //child
// close(listensock_);
// if(fork() > 0)
// exit(0);
// //grandchild
// Service(sockfd, clientip, clientport);
// close(sockfd);
// exit(0);
// }
// close(sockfd);
// pid_t rid = waitpid(id, nullptr, 0);
// if(rid == id)
// std::cout << "father wait success" << std::endl;
//version 3 多线程版
ThreadData* td = new ThreadData(sockfd, clientip, clientport, this);
pthread_t tid;
pthread_create(&tid, nullptr, Routine, td);
}
}
测试





ps -aL | head -1 && ps -aL | grep tcp

三、源代码
makefile
all:tcpserver tcpclient
tcpserver:Main.cc
g++ -o $@ $^ -std=c++11 -lpthread
tcpclient:TcpClient.cc
g++ -o $@ $^ -std=c++11
.PHONT:clean
clean:
rm -f tcpserver tcpclient
TcpServer.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Log.hpp"
const int defaultfd = –1;
const uint16_t defaultport = 8080;
const std::string defaultip = "0.0.0.0";
const int backlog = 10;
extern Log lg;
enum
{
UsageError = 1,
SocketError,
BindError,
ListenError
};
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, const std::string& ip, const uint16_t& p, TcpServer* t)
: sockfd(fd)
, clientip(ip)
, clientport(p)
, tsvr(t)
{}
public:
int sockfd;
std::string clientip;
uint16_t clientport;
TcpServer* tsvr;
};
class TcpServer
{
public:
TcpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
: listensock_(defaultfd), port_(port), ip_(ip)
{}
void InitServer()
{
listensock_ = socket(AF_INET, SOCK_STREAM, 0);
if (listensock_ < 0)
{
lg(Fatal, "create socket error, errno: %d, errstring: %s", errno, strerror(errno));
exit(SocketError);
}
lg(Info, "create socket success, listensock_: %d", listensock_);
//防止偶发性服务器无法立即重启
int opt = 1;
setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port_);
inet_aton(ip_.c_str(), &(server.sin_addr));
socklen_t len = sizeof(server);
if (bind(listensock_, (struct sockaddr *)&server, len) < 0)
{
lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
exit(BindError);
}
lg(Info, "bind socket success, listensock_: %d", listensock_);
if (listen(listensock_, backlog) < 0)
{
lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
exit(ListenError);
}
lg(Info, "listen socket success, listensock_: %d", listensock_);
}
static void* Routine(void* args)
{
pthread_detach(pthread_self());
ThreadData* td = static_cast<ThreadData*>(args);
td->tsvr->Service(td->sockfd, td->clientip, td->clientport);
close(td->sockfd);
delete td;
return nullptr;
}
void StartServer()
{
lg(Info, "tcpserver is running…");
for (;;)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);
if (sockfd < 0)
{
lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
continue;
}
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
lg(Info, "get a new link…, sockfd: %d, client ip: %s, client port: %d",
sockfd, clientip, clientport);
//version 1 单进程/单线程版
// Service(sockfd, clientip, clientport);
// close(sockfd);
//version 2 多进程版
// pid_t id = fork();
// if(id == 0)
// {
// //child
// close(listensock_);
// if(fork() > 0)
// exit(0);
// //grandchild
// Service(sockfd, clientip, clientport);
// close(sockfd);
// exit(0);
// }
// close(sockfd);
// pid_t rid = waitpid(id, nullptr, 0);
// if(rid == id)
// std::cout << "father wait success" << std::endl;
//version 3 多线程版
ThreadData* td = new ThreadData(sockfd, clientip, clientport, this);
pthread_t tid;
pthread_create(&tid, nullptr, Routine, td);
}
}
void Service(int sockfd, const std::string& clientip, const uint16_t& clientport)
{
while (true)
{
char inbuffer[4096];
ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) – 1);
if (n > 0)
{
inbuffer[n] = 0;
std::cout << "client say# " << inbuffer << std::endl;
std::string echo_string = "tcpserver echo# ";
echo_string += inbuffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
else if(n == 0)
{
lg(Info, "%s:%d quit, server colse sockfd: %d", clientip.c_str(), clientport, sockfd);
break;
}
else
{
//异常
lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d",
sockfd, clientip.c_str(), clientport);
break;
}
}
}
~TcpServer()
{
if (listensock_ > 0)
close(listensock_);
}
private:
int listensock_;
uint16_t port_;
std::string ip_;
};
Main.cc
#include <iostream>
#include <memory>
#include "TcpServer.hpp"
void Usage(const std::string str)
{
std::cout << "\\n\\tUsage: " << str << " port[1024+]\\n" << std::endl;
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(UsageError);
}
uint16_t port = std::stoi(argv[1]);
std::unique_ptr<TcpServer> server(new TcpServer(port));
server->InitServer();
server->StartServer();
return 0;
}
TcpCilent.cc
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void Usage(const std::string& str)
{
std::cout << "\\n\\tUsage: " << str << " serverip serverport" << std::endl;
}
// ./tcpclient serverip serverport
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 0;
}
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 create err" << 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));
socklen_t len = sizeof(server);
//客户端发起connect请求的时候,操作系统会自动进行端口号的随机bind绑定
int n = connect(sockfd, (struct sockaddr*)&server, len);
if(n < 0)
{
std::cerr << "connect err…" << std::endl;
return 2;
}
std::string message;
char inbuffer[4096];
while(true)
{
std::cout << "Please Enter# ";
std::getline(std::cin, message);
int n = write(sockfd, message.c_str(), message.size());
if(n < 0)
{
std::cerr << "write err" << std::endl;
break;
}
n = read(sockfd, inbuffer, sizeof(inbuffer) – 1);
if(n > 0)
{
inbuffer[n] = 0;
std::cout << inbuffer << std::endl;
}
else
{
break;
}
}
close(sockfd);
return 0;
}
Log.hpp
#pragma once
#include <iostream>
#include <string>
#include <ctime>
#include <cstdio>
#include <cstdarg>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.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;
}
~Log()
{}
std::string levelToString(int level)
{
switch(level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warning";
case Error:
return "Error";
case Fatal:
return "Fata";
default:
return "";
}
}
void operator()(int level, const char* format, ...)
{
//默认部分 = 日志等级 + 日志时间
time_t t = time(nullptr);
struct tm* ctime = localtime(&t);
char leftbuffer[SIZE];
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
va_list s;
va_start(s, format);
char rightbuffer[SIZE];
vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
va_end(s);
char logtxt[2 * SIZE];
snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);
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);
}
private:
int printMethod;
std::string path;
};
Log lg;
总结
以上就是今天的博客内容啦,希望对读者朋友们有帮助 水滴石穿,坚持就是胜利,读者朋友们可以点个关注 点赞收藏加关注,找到小编不迷路!




