本章Gitee倉(cāng)庫(kù):tcp套接字
主要代碼
客戶端:
#pragma once
#include"Log.hpp"
#include<iostream>
#include<cstring>
#include<sys/wait.h>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include"threadPool.hpp"
#include"Task.hpp"
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; //不要設(shè)置太大
Log log;
enum{
USAGE_ERR = 1,
SOCKET_ERR,
BIND_ERR,
LITSEN_ERR
};
class TcpServer;
class ThreadData
{
public:
ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
:t_sockfd_(fd), t_clientip_(ip), t_clientport_(port), t_tsvr_(t)
{}
public:
int t_sockfd_;
std::string t_clientip_;
uint16_t t_clientport_;
TcpServer *t_tsvr_; //需要this指針
};
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip)
:listensockfd_(defaultfd)
,port_(port)
,ip_(ip)
{}
//初始化服務(wù)器
void Init()
{
//創(chuàng)建套接字
listensockfd_ = socket(AF_INET, SOCK_STREAM, 0); //sock_stream提供字節(jié)流服務(wù)--tcp
if(listensockfd_ < 0)
{
log(Fatal, "create socket, errno: %d, errstring: %s",errno, strerror(errno));
exit(SOCKET_ERR);
}
log(Info, "create socket success, sockfd: %d",listensockfd_);
int opt = 1;
setsockopt(listensockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); //防止偶發(fā)性服務(wù)器無(wú)法進(jìn)行立即重啟
//本地套接字信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
//填充網(wǎng)絡(luò)信息
local.sin_family = AF_INET;
local.sin_port = htons(port_);
inet_aton(ip_.c_str(), &(local.sin_addr));
//bind
int bd = bind(listensockfd_, (struct sockaddr*)&local, sizeof(local));
if(bd < 0)
{
log(Fatal, "bind error, errno: %d, errstring: %s",errno, strerror(errno));
exit(BIND_ERR);
}
log(Info, "bind success");
//tcp面向連接, 通信之前要建立連接
//監(jiān)聽(tīng)
if(listen(listensockfd_, backlog) < 0)
{
log(Fatal, "listen error, errno: %d, errstring: %s",errno, strerror(errno));
exit(LITSEN_ERR);
}
log(Info, "listen success");
}
static void *Routine(void *args)
{
pthread_detach(pthread_self());
//子線程無(wú)需關(guān)閉文件描述符
ThreadData *td = static_cast<ThreadData*>(args);
td->t_tsvr_->Service(td->t_sockfd_, td->t_clientip_, td->t_clientport_);
delete td;
}
void Start()
{
signal(SIGPIPE, SIG_IGN);
threadPool<Task>::GetInstance()->Start();
//signal(SIGCHLD, SIG_IGN); //直接忽略進(jìn)程等待 V2
log(Info, "server is running...");
while(true)
{
//獲取新鏈接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
if(sockfd < 0)
{
log(Warning, "accpet 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));
log(Info, "get a new link..., sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);
//根據(jù)新鏈接進(jìn)行通信
//V1--單進(jìn)程
//Service(sockfd, clientip, clientport);
//close(sockfd);
//V2--多進(jìn)程
// pid_t id = fork();
// if(id == 0)
// {
// //child
// close(listensockfd_); //子進(jìn)程可以看到父進(jìn)程的文件描述符表,關(guān)閉不需要的
// if(fork() > 0) exit(0);//父進(jìn)程創(chuàng)建的子進(jìn)程直接退出
// Service(sockfd, clientip, clientport); //孫子進(jìn)程執(zhí)行, 由于孫子的父進(jìn)程退出, 由系統(tǒng)領(lǐng)養(yǎng)
// close(sockfd);
// exit(0);
// }
// //father
// close(sockfd); //存在引用計(jì)數(shù),不會(huì)這個(gè)關(guān)閉這個(gè)文件
// pid_t tid = waitpid(id, nullptr, 0);
// (void)tid;
//V3--多線程(創(chuàng)建進(jìn)程成本較高,換線程)
// ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
// pthread_t tid;
// pthread_create(&tid, nullptr, Routine, td);
//pthread_join(tid, nullptr); //已進(jìn)行線程分離,無(wú)需等待(并發(fā)執(zhí)行)
//V4--線程池
Task t(sockfd, clientip, clientport);
threadPool<Task>::GetInstance()->Push(t);
//sleep(1);
}
}
void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
{
char buffer[4096];
while(true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer));
if(n > 0)
{
buffer[n] = 0;
std::cout << "client say# " << buffer << std::endl;
std::string echo_str = "tcpserver echo# ";
echo_str += buffer;
write(sockfd, echo_str.c_str(), echo_str.size());
}
else if(n == 0)
{
log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
break;
}
else
{
log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);
break;
}
memset(buffer, 0, sizeof(buffer));
}
}
~TcpServer(){}
private:
int listensockfd_;
uint16_t port_;
std::string ip_;
};
客戶端:
#include<iostream>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
void Usage(const std::string &proc)
{
std::cout << "\n\tUsage: " << proc << "serverip serverport" << std::endl;
}
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 sockfd = 0;
int cnt = 5;
bool isreconnect = false;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
return 1;
}
do
{
// tcp客戶端也無(wú)需顯示bind
// 向目標(biāo)發(fā)起連接(connect的時(shí)候進(jìn)行自動(dòng)隨機(jī)bind)
int cn = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
if (cn < 0)
{
isreconnect = true;
cnt--; //重連5秒
std::cerr << "connet error, reconnect: " << cnt << std::endl;
close(sockfd);
sleep(1); //每隔一秒重連一次
}
else
{
break;
}
}while(cnt && isreconnect);
if(cnt == 0)
{
std::cerr << "server offline..." << std::endl;
break;
}
//服務(wù)模塊
//while (true)
{
std::string message;
std::cout << "Please Enter# ";
std::getline(std::cin, message);
if (message.empty())
continue;
int wn = write(sockfd, message.c_str(), message.size());
if (wn < 0)
{
std::cerr << "write error" << std::endl;
//break;
}
char inbuffer[4096];
int rn = read(sockfd, inbuffer, sizeof(inbuffer));
if (rn > 0)
{
inbuffer[rn] = 0;
std::cout << inbuffer << std::endl;
}
else
{
//break;
}
}
close(sockfd);
}
return 0;
}
關(guān)于構(gòu)造
關(guān)于構(gòu)造和初始化,可以直接在構(gòu)造的時(shí)候,將服務(wù)器初始化,那為什么還要寫到init
初始化函數(shù)里面呢?
構(gòu)造盡量簡(jiǎn)單一點(diǎn),不要做一些“有風(fēng)險(xiǎn)”的操作。
listen監(jiān)聽(tīng)
tcp
是面向連接的,通信之前要建立連接,服務(wù)器處于等待連接到來(lái)的狀態(tài):
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
accept
獲取新鏈接:
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
后兩個(gè)參數(shù)輸出型參數(shù),獲取對(duì)方的信息
這里返回也是一個(gè)套接字,這和最開(kāi)始創(chuàng)建的listensockfd_
有什么區(qū)別呢?
這就好比去吃飯,門口有一個(gè)拉客的,問(wèn)“帥哥,來(lái)不來(lái)我們這里吃飯啊”,如果你去了,那就是“歡迎光臨,幾位呀”,你回答“我和我女朋友”,然后這個(gè)人大喊“兩位客人,來(lái)一個(gè)服務(wù)員”,這時(shí)候就來(lái)了一個(gè)服務(wù)員招呼你坐下,然后給你菜單,點(diǎn)完菜之后給你上菜;在這個(gè)期間站在門口的拉客的人,一直在招攬客人,有人來(lái)就喊服務(wù)員招招待,人家不來(lái)就繼續(xù)拉。
listensockdf_
就是這個(gè)拉客的,真正給我們提供服務(wù)的是accept
返回的
telnet測(cè)試
telnet
可以對(duì)指定服務(wù)的遠(yuǎn)程連接
127.0.0.1
本地環(huán)回,再加上端口號(hào)就可以測(cè)試了
ctrl + ]
,然后回車
讀取信息
tcp
是面向字節(jié)流的,管道是面向字節(jié)流的、讀取普通文件也是面向字節(jié)流的,所以可以采用read
進(jìn)行讀取。
掉線重連
當(dāng)讀端關(guān)閉之后,系統(tǒng)會(huì)殺掉寫端,采用signal(SIGPIPE, SIG_IGN)
忽略這個(gè)信號(hào)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-829838.html
翻譯服務(wù)器演示
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-829838.html
到了這里,關(guān)于Linux網(wǎng)絡(luò)編程——tcp套接字的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!