上一篇:https://blog.csdn.net/qq_41938259/article/details/104916488基本实现了TCP服务器的建立,它支持多个用户的接入。但是一遍遍写创建sock、监听、接收、发送这些语句,令人十分烦躁,还好C++是面向对象编程,拥有继承、封装、多态等特性,我们可以建立一个XTcp.h头文件,用于建立XTcp类,而新建一个XTcp.cpp文件用于实现Xtcp.h中的成员函数,以支持重复使用,减少不必要的劳动,如下所示:
XTcp.h文件如下:
#pragma once
#include<string>
#include<cstring>
using namespace std;
class XTcp
{
public:
int CreateSocket();
bool Bind(unsigned short port);
XTcp Accept();
void Close();
int Recv(char*buf,int bufsize);
int Send(const char*buf,int sendsize);
XTcp();
virtual ~XTcp() {};
int sock = 0;
unsigned short port = 0;
string ip;
};
XTcp.cpp文件:
#include "XTcp.h"
#include <iostream>
#include<thread>
#include<ws2tcpip.h>
#include<Windows.h>
#include<string>
#include<string.h>
XTcp::XTcp()
{
static bool first = true;
if (first)
{
first = false;
//初始化动态链接库
WSADATA ws;
WSAStartup(MAKEWORD(2, 2), &ws);//22是版本号,加载动态链接库
}
}
int XTcp::CreateSocket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);//AF_INET指明调用TCP/IP协议,SOCK_STREAM是TCP的协议(相对于UDP来讲)
//失败提示
if (sock == -1)
{
cout << "create socket failed!" << endl;
}
return sock;//出错也可以返回错误内容
}
bool XTcp::Bind(unsigned short port)
{
if (sock <= 0)
CreateSocket();
//创建TCP相关的结构体
sockaddr_in saddr;
saddr.sin_family = AF_INET;//使用TCP
saddr.sin_port = htons(port);//本地字节序转网络字节序
//X86架构是小端的而网络字节流是大端的,
//Linux不一定,小型linux使用的也是和网路字节序一样的话转换也只是一个空的宏,
//这时候会可有可无,但考虑兼容性要求建议加上
saddr.sin_addr.s_addr = htonl(INADDR_ANY);//这里可以指定网卡,0是任意的意思
if (::bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0)//绑定端口号到上面创建的socket,并判断是否成功
{
cout << "bind port " << port << " failed!" << endl;
return false;
}
cout << "bind port " << port << " success!" << endl;
listen(sock, 10);//监听,接受连接;10是列表大小,套接字接收队列的最大大小
//accept每调用一次队列就会减少一个
return true;
}
XTcp XTcp::Accept()
{
XTcp tcp;
sockaddr_in caddr;
int len = sizeof(caddr);
int client = accept(sock, (sockaddr*)&caddr, &len);//取信息
if (client <= 0)return tcp;//tcp初始值为0,返回0则是失败
cout <<"accept client " << client << endl;
tcp.ip = inet_ntoa(caddr.sin_addr);
tcp.port = ntohs(caddr.sin_port);//网络字节序转本地字节序
cout << "client ip is " << tcp.ip << " port is " << tcp.port << endl;
return tcp;
}
void XTcp::Close()
{
if (sock <= 0)return;
closesocket(sock);
}
int XTcp::Recv(char* buf, int bufsize)
{
return recv(sock, buf, bufsize, 0);
}
int XTcp::Send(const char* buf, int size)
{
int s = 0;
while (s!=size)
{
int len = send(sock, buf + s, size - s, 0);
if (len <= 0)break;
s += len;
}
return s;
}
可以看到,我们只是将上一次的写的代码逐个再写道XTcp.cpp中去而已,此时main文件就简洁多了:
#include <iostream>
#include<thread>
#include<Windows.h>
#include<string>
#include<string.h>
#include"XTcp.h"
using namespace std;
class tcpthread
{
public:
void Main()
{
char buf[1024] = { 0 };//接收信息的最大长度,记位buf
while (true)
{
int recvlen = client.Recv(buf, sizeof(buf) - 1);//windows没有read函数,linux才有
if (recvlen <= 0)break;//没有收到
buf[recvlen] = '\0';
if (strstr(buf, "q") != NULL)//按q退出
{
char re[] = "quit success!!!\n";
client.Send(re, strlen(re) + 1);
break;
}
int sendlen = client.Send("ok\n", 4);//linux可以使用write
cout << "receive:" << buf << endl;
//len是接收数据的实际大小,len<=buf长度(这里是1024)
}
client.Close();//关闭连接
delete this;
}
XTcp client;
};
int main(int argc, char* argv[])
{
//测试端口号
unsigned short port = 8080;
if (argc > 1)
{
port = atoi(argv[1]);
}
XTcp server;
server.CreateSocket();
server.Bind(port);
while(true)
{
XTcp client = server.Accept();
tcpthread *th = new tcpthread();
th->client = client;
thread sth(&tcpthread::Main, th);
sth.detach();//释放主线程拥有的子线程的资源
}
server.Close();
return 0;
}
这是本次的测试截图,一共两个接入:
END