C++socket编程学习总结(3)封装TCP服务器的几个常用方法

上一篇: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

发布了162 篇原创文章 · 获赞 38 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41938259/article/details/104987898
今日推荐