您现在的位置是:首页 >技术教程 >tcp套接字的应用网站首页技术教程
tcp套接字的应用
简介tcp套接字的应用
tcp服务端流程
tcp客户端流程
客户端代码
tcpClient.hpp
#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
namespace szg
{
class tcpClient
{
public:
tcpClient(string ip, uint16_t port)
:_ip(ip), _port(port)
{
_sock = socket(AF_INET, SOCK_STREAM, 0);
if(_sock < 0)
{
cerr << "用户端创建套接字失败" << endl;
exit(-1);
}
cout << "用户端创建套接字成功" << endl;
}
void start()
{
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(_port);
server.sin_addr.s_addr = inet_addr(_ip.c_str());
if(connect(_sock, (sockaddr*)&server, sizeof(server)))
{
cerr << "客户端获取服务失败" << endl;
}
cout << "客户端获取服务成功" << endl;
while(true)
{
string message;
while(true)
{
getline(cin, message);
write(_sock, message.c_str(), message.size());
char rvbuf[1024];
size_t n = read(_sock, rvbuf, sizeof(rvbuf) - 1);
rvbuf[n] = 0;
if(0 == n)
{
cerr << "server qiut , me too" << endl;
break;
}
cout << "server 回显:" << rvbuf << endl;
}
}
}
private:
int _sock;
string _ip;
uint16_t _port;
};
}
tcpClient.cc
#include"tcpClient.hpp"
#include<memory>
int main(int args, char* argv[])
{
if(args != 3)
{
cout << "
Usage:
" << argv[0] << " serverip serverport
";
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
unique_ptr<szg::tcpClient> tcpclient(new szg::tcpClient(ip, port));
tcpclient->start();
return 0;
}
客户端进程版
tcpServer.hpp
#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <string.h>
#include <errno.h>
using namespace std;
namespace szg
{
string df_ip = "0.0.0.0";
uint16_t df_port = 8080;
class tcpServer
{
public:
tcpServer(uint16_t port = df_port)
: _port(port)
{
//1.创建套接字
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if(_listensock < 0)
{
cerr << "服务端创建套接字失败" << endl;
exit(-1);
}
cout << "服务端创建套接字成功" << endl;
}
void start()
{
//2.绑定套接字
sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(_port);
server.sin_addr.s_addr = INADDR_ANY;
if(-1 == bind(_listensock, (sockaddr*)&server, sizeof(server)))
{
cerr << "服务端绑定监听套接字失败" << endl;
perror("server bind :");
exit(1);
}
cout << "服务端绑定监听套接字成功" << endl;
//3.设置套接字为监听状态
if(listen(_listensock, 5))
{
cerr << "服务器监听状态设置失败" << endl;
exit(2);
}
cout << "服务器监听状态设置成功" << endl;
signal(SIGCHLD, SIG_IGN);
while(true)
{
sockaddr_in peer;
socklen_t len = sizeof(peer);
//4.建立连接
int newsock = accept(_listensock, (sockaddr*)&peer, &len);
if(newsock < 0)
{
cerr << "服务器监听客户端失败" << endl;
}
cout << "服务器监听客户端成功" << endl;
if(0 == fork())
{
char rdbuf[1024];
while (true)
{
size_t n = read(newsock, rdbuf, sizeof(rdbuf) - 1);
if(n == 0)
{
cout << "client offline, me too" << endl;
break;
}
rdbuf[n] = 0;
write(newsock, rdbuf, strlen(rdbuf));
}
exit(0);
}
close(newsock);
}
}
private:
int _listensock;
uint16_t _port;
};
}
tcpServer.cc
#include"tcpServer.hpp"
#include<memory>
int main(int args, char* argv[])
{
if(args != 2)
{
cout << "
server:
" << argv[0] << " serverport
";
}
uint16_t port = atoi(argv[1]);
unique_ptr<szg::tcpServer> tcpserver(new szg::tcpServer(port));
tcpserver->start();
return 0;
}
客户端线程池版
MutexGuard.hpp
#pragma once
#include<pthread.h>
class MutexGuard
{
public:
MutexGuard(pthread_mutex_t* pmutex)
:_pmutex(pmutex)
{
pthread_mutex_lock(_pmutex);
}
~MutexGuard()
{
pthread_mutex_unlock(_pmutex);
}
private:
pthread_mutex_t* _pmutex;
};
mythread.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
#include <functional>
#include <pthread.h>
class Thread;
//context传递对象的地址,帮助静态成员函数可以使用类内成员变量
class context
{
public:
context(Thread* t = nullptr, void* args = nullptr)
:_this(t)
,_args(args)
{}
Thread* _this;
void* _args;
};
class Thread
{
public:
Thread(const std::function<void*(void*)>& f, void* args = nullptr, const std::string& name = "")
:_func(f)
,_args(args)
,_name(name)
{
++_num;
if(_name.size() == 0)
{
_name += "thread ";
_name += std::to_string(_num);
}
//创建上下文结构体
context* pcon = new context(this, _args);
int n = pthread_create(&_tid, nullptr, route_start, pcon);
assert(0 == n);
(void)n;
}
static void* route_start(void* pcon)
{
context* cont = static_cast<context*>(pcon);
void* ret = cont->_this->run(cont->_args);
delete cont;
return ret;
}
void* run(void* args)
{
return _func(args);
}
void join()
{
int n = pthread_join(_tid, nullptr);
assert(n == 0);
(void)n;
}
std::string name()
{
return _name;
}
~Thread()
{
--_num;
}
private:
pthread_t _tid;
std::string _name;
void* _args;
std::function<void*(void*)> _func;
static int _num;
};
int Thread::_num = 0;
mythreadpool.hpp
#pragma once
#include<vector>
#include<queue>
#include<mutex>
#include"mythread.hpp"
#include"MutexGuard.hpp"
const int maxnum = 5;
template<class T>
class threadpool;
//上下文
template<class T>
class condate
{
public:
condate(T* t, int num)
:_this(t), _num(num)
{}
T* _this;
int _num;
};
template<class T>
class threadpool
{
private:
static void* task_handler(void* args)
{
condate<threadpool<T>>* pcondate = static_cast<condate<threadpool<T>>* >(args);
threadpool<T>* tp = pcondate->_this;
while(true)
{
T t;
tp->pop(t);
std::cout << tp->_threads[pcondate->_num]->name() << " : ";
t();
}
delete pcondate;
}
threadpool(int num = maxnum)
:_num(num)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_cond, nullptr);
for(int i = 0; i < _num; ++i)
{
condate<threadpool<T>>* ct = new condate<threadpool<T>>(this, i);
_threads.push_back(new Thread(task_handler, ct));
}
}
public:
void pop(T& t)
{
MutexGuard mg(&_mutex);
while(_task.empty())
pthread_cond_wait(&_cond, &_mutex);
t = _task.front();
_task.pop();
}
void push(const T& t)
{
MutexGuard mg(&_mutex);
_task.push(t);
pthread_cond_signal(&_cond);
}
~threadpool()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
for(auto& t : _threads)
{
delete t;
}
}
static threadpool<T>* getinstance()
{
if(nullptr == _singleton)
{
_singletonlock.lock();
if(nullptr == _singleton)
{
_singleton = new threadpool();
}
_singletonlock.unlock();
}
return _singleton;
}
private:
int _num;
std::vector<Thread*> _threads;
std::queue<T> _task;
pthread_mutex_t _mutex;
pthread_cond_t _cond;
static std::mutex _singletonlock;
static threadpool<T>* _singleton;
};
template<class T>
std::mutex threadpool<T>::_singletonlock;
template<class T>
threadpool<T>* threadpool<T>::_singleton = nullptr;
tcpServer.hpp
#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"mythreadpool.hpp"
#include"log.hpp"
#include <string.h>
#include <errno.h>
using namespace std;
namespace szg
{
class tcp_task
{
public:
tcp_task(int sock = 0)
:_sock(sock)
{}
void operator()()
{
char rdbuf[1024];
while (true)
{
size_t n = read(_sock, rdbuf, sizeof(rdbuf) - 1);
if(n == 0)
{
cout << "client offline, me too" << endl;
break;
}
rdbuf[n] = 0;
write(_sock, rdbuf, strlen(rdbuf));
}
close(_sock);
}
int _sock;
};
string df_ip = "0.0.0.0";
uint16_t df_port = 8080;
class tcpServer
{
public:
tcpServer(uint16_t port = df_port)
: _port(port)
{
//1.创建套接字
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if(_listensock < 0)
{
cerr << "服务端创建套接字失败" << endl;
exit(-1);
}
cout << "服务端创建套接字成功" << endl;
logMessage(NORMAL, "accept a new link success, get new sock: %d", _listensock); // ?
logMessage(DEBUG, "accept error, next");
logMessage(WARNING, "accept error, next");
logMessage(FATAL, "accept error, next");
logMessage(NORMAL, "accept error, next");
logMessage(NORMAL, "accept a new link success, get new sock: %d", _listensock); // ?
logMessage(DEBUG, "accept error, next");
logMessage(WARNING, "accept error, next");
logMessage(FATAL, "accept error, next");
logMessage(NORMAL, "accept error, next");
logMessage(NORMAL, "accept a new link success, get new sock: %d", _listensock); // ?
logMessage(DEBUG, "accept error, next");
logMessage(WARNING, "accept error, next");
logMessage(FATAL, "accept error, next");
logMessage(NORMAL, "accept error, next");
}
void start()
{
//2.绑定套接字
sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(_port);
server.sin_addr.s_addr = INADDR_ANY;
if(-1 == bind(_listensock, (sockaddr*)&server, sizeof(server)))
{
cerr << "服务端绑定监听套接字失败" << endl;
perror("server bind :");
exit(1);
}
cout << "服务端绑定监听套接字成功" << endl;
//3.设置套接字为监听状态
if(listen(_listensock, 5))
{
cerr << "服务器监听状态设置失败" << endl;
exit(2);
}
cout << "服务器监听状态设置成功" << endl;
signal(SIGCHLD, SIG_IGN);
while(true)
{
sockaddr_in peer;
socklen_t len = sizeof(peer);
//4.建立连接
int newsock = accept(_listensock, (sockaddr*)&peer, &len);
if(newsock < 0)
{
cerr << "服务器监听客户端失败" << endl;
}
cout << "服务器监听客户端成功:" << newsock << endl;
threadpool<tcp_task>::getinstance()->push(tcp_task(newsock));
// if(0 == fork())
// {
// char rdbuf[1024];
// while (true)
// {
// size_t n = read(newsock, rdbuf, sizeof(rdbuf) - 1);
// if(n == 0)
// {
// cout << "client offline, me too" << endl;
// break;
// }
// rdbuf[n] = 0;
// write(newsock, rdbuf, strlen(rdbuf));
// }
// exit(0);
// }
// close(newsock);
}
}
private:
int _listensock;
uint16_t _port;
};
}
日志记录文件
log.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#define LOG_NORMAL "log.txt"
#define LOG_ERR "log.error"
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
const char * to_levelstr(int level)
{
switch(level)
{
case DEBUG : return "DEBUG";
case NORMAL: return "NORMAL";
case WARNING: return "WARNING";
case ERROR: return "ERROR";
case FATAL: return "FATAL";
default : return nullptr;
}
}
void logMessage(int level, const char* formate, ...)
{
char logprefix[1024];
sprintf(logprefix, "[%s][time:%ld][pid:%d]",to_levelstr(level), (long long)time(nullptr), getpid());
char logbackfix[1024];
va_list arg;
va_start(arg, formate);
vsnprintf(logbackfix, sizeof(logbackfix) - 1, formate, arg);
FILE *log = fopen(LOG_NORMAL, "a");
FILE *err = fopen(LOG_ERR, "a");
if(log != nullptr && err != nullptr)
{
FILE *curr = nullptr;
if(level == DEBUG || level == NORMAL || level == WARNING) curr = log;
if(level == ERROR || level == FATAL) curr = err;
if(curr) fprintf(curr, "%s%s
", logprefix, logbackfix);
fclose(log);
fclose(err);
}
}
守护进程
ctrl + z将前台引用放到后台(暂停)
jobs 查看作业
fg + 作业号(切到前台) <————> bg + 作业号(启动暂停的作业号)
守护进程,精灵进程的本质是孤儿进程。
守护进程化
setsid: 中间新建一个会话,必须不是作业的组长才能使用
1. 让调用进程忽略掉异常的信号
2. 如何让自己不是组长,setsid且创建子进程
3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件
4.更改可执行路径
#pragma once
#include <unistd.h>
#include <signal.h>
#include <cstdlib>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define DEV "/dev/null"
void daemonSelf(const char *currPath = nullptr)
{
// 1. 让调用进程忽略掉异常的信号
signal(SIGPIPE, SIG_IGN);
// 2. 如何让自己不是组长,setsid
if (fork() > 0)
exit(0);
// 子进程 -- 守护进程,精灵进程,本质就是孤儿进程的一种!
pid_t n = setsid();
assert(n != -1);
// 3. 守护进程是脱离终端的,关闭或者重定向以前进程默认打开的文件
int fd = open(DEV, O_RDWR);
if(fd >= 0)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
else
{
close(0);
close(1);
close(2);
}
// 4. 可选:进程执行路径发生更改
if(currPath) chdir(currPath);
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。