一、服务端
1.tcpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
enum
{
USAGE_ERR = 1,
SOCKET_ERR,
BIND_ERR,
LISTEN_ERR
};
static const uint16_t gport = 8080;
static const int gbacklog = 5;
class TcpServer;
class ThreadData
{
public:
ThreadData(TcpServer *self, int sock)
: _self(self), _sock(sock)
{}
public:
TcpServer *_self;
int _sock;
};
class TcpServer
{
public:
// 构造函数
TcpServer(const uint16_t &port = gport)
: _listensock(-1), _port(port)
{}
// 析构函数
~TcpServer() {}
// 初始化服务器
void initServer()
// 启动服务器
void start()
private:
int _listensock; // 不是用来进行数据通信的,他是用来监听链接到来,获取新链接的!
uint16_t _port; // 端口号
};
(1)initServer()
void initServer()
{
// 1.创建socket文件套接字对象
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
logMessage(FATAL, "create socket error");
exit(SOCKET_ERR);
}
logMessage(NORMAL, "create socket success:%d", _listensock); // ? 后面可能会加东西
// 2.bind绑定自己的网络信息
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;
if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
logMessage(FATAL, "bind socket error");
exit(BIND_ERR);
}
logMessage(NORMAL, "bind socket success");
// 3.设置socket 为监听状态
if (listen(_listensock, gbacklog) < 0) // listen第二个参数以后在讲
{
logMessage(FATAL, "listen socket error");
exit(LISTEN_ERR);
}
logMessage(NORMAL, "listen socket success");
}
(2)start()
void start()
{
for (;;)
{
// 4.server 获取新链接
// sock 才是和client进行通信的fd
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
if (sock < 0)
{
logMessage(ERROR, "accept error, next");
continue;
}
logMessage(NORMAL, "accept a new link success, get new sock:%d", sock);
// 5.这里就是一个sock,未来通信我们就用这个sock,面向字节流的,后续全部都是文件操作!
serviceIO(sock);
close(sock); // 对一个已经使用完毕的sock,我们要关闭这个sock,要不然会导致,文件描述符泄露
}
}
2.tcpServer.cpp
#include "tcpServer.hpp"
#include <memory>
using namespace std;
// 使用手册
static void Usage(string proc)
{
cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
// ./tcpServer local_port
int main(int argc, char *argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
// tcp服务器的调用逻辑
unique_ptr<TcpServer> tsvr(new TcpServer(port));
tsvr->initServer();
tsvr->start();
return 0;
}
二、客户端
1.tcpClient.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define NUM 1024
class TcpClient
{
public:
// 构造函数
TcpClient(const std::string &serverip, const uint16_t &serverport)
:_sock(-1), _serverip(serverip), _serverport(serverport)
{}
// 析构函数
~TcpClient()
{
if(_sock >= 0)
close(_sock);
}
// 初始化客户端
void initClient()
// 启动客户端
void start()
private:
int _sock;
std::string _serverip;
uint16_t _serverport;
};
(1)initClient()
void initClient()
{
// 1.创建socket
_sock = socket(AF_INET, SOCK_STREAM, 0);
if (_sock < 0)
{
std::cerr << "socket create error" << std::endl;
exit(2);
}
// 2.tcp的客户端要不要bind?要的! 但是要不要显示的bind?不要!尤其这里client port要让OS自己随机指定!
// 3.要不要listen?不要!
// 4.要不要accept?不要!
}
(2)start()
void start()
{
// 5.要发起连接!
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(_serverport);
server.sin_addr.s_addr = inet_addr(_serverip.c_str());
if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) != 0)
{
std::cerr << "socket connet error" << std::endl;
}
else
{
// 成功连接
std::string msg;
while (true)
{
std::cout << "Enter# ";
std::getline(std::cin, msg);
write(_sock, msg.c_str(), msg.size());
char buffer[NUM];
int n = read(_sock, buffer, sizeof(buffer) - 1);
if (n > 0)
{
// 目前我们可以吧读到的数据当成字符串
buffer[n] = 0;
std::cout << "Server回显# " << buffer << std::endl;
}
else
{
break;
}
}
}
}
2.tcpClient.cpp
#include "tcpClient.hpp"
#include <memory>
// 使用手册
static void Usage(std::string proc)
{
std::cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
// ./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 = atoi(argv[2]);
std::unique_ptr<TcpClient> tcli(new TcpClient(serverip, serverport));
tcli->initClient();
tcli->start();
return 0;
}