Linux下基于C++11的socket网络编程(基础版本)

本文档详细介绍了C++中使用socket进行TCP网络编程的学习过程,包括基础知识、C++11 std::thread 线程库的简单封装、客户端和服务端的实现。作者通过五个阶段逐步讲解,从基础版的回声服务器到多线程服务器,涉及select和epoll等机制。此外,还提供了客户端和服务端的完整代码示例,适合TCP/IP网络编程初学者入门。
摘要由CSDN通过智能技术生成

第一:socket的基础知识

略,网上有很多这样的知识,我觉得他们应该讲的比我好。我是跟着韩国人尹圣雨写的《TCP/IP网络编程》这本书学的。

第二:使用的线程库

C++11 std::thread

在经过自己简单的封装

第三:声明

        因为我也是初学,可能写的不好,封装的也不好,我写这篇文章,只是希望帮助很基础的初学者,慢慢的接触socket,也给自己记录一下学习的经过。

        所以,如果错误的,或者不好的地方,望各位多多包涵,一起学习。

        我是使用cMake构建项目的,我也只会很简单很简单的,这里就不贴出来了。如你不会的话,就直接用g++编译就好了。(可以百度一下g++多文件的编译)

        这个我分为五篇文章,每篇文章是一个完整的项目

        第一个是基础版的,也就是进行简单的封装,客户端连接服务端(仅限一个),做一个回声服务器。

        第二个是使用多进程服务器,可进行多用户的连接。

        第三个是使用select函数。

        第四个是使用epoll。

        第五个是多线程服务器。

第四:代码

客户端

/*================================================================
*   Copyright (C) 2021 ymbLite. All rights reserved.
*   
*   文件名称:TcpClient.h
*   创 建 者:ymbLite
*   创建日期:2021年11月03日
*   描    述:
*
================================================================*/


#ifndef _TCPCLIENT_H
#define _TCPCLIENT_H

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <unistd.h>
#include <netdb.h>

#define BUFF_SIZE 1024

using std::cout;
using std::cin;
using std::endl;
using std::string;

class TcpClient{
	private:
		int clnt_sock;			//客户端的socket
		struct sockaddr_in serv_addr;	//客户端的连接服务器的网络地址结构
		char buf[BUFF_SIZE];		//客户端的读写缓冲区
		int buf_len;			//客户端的读写长度
	public:
		TcpClient();//构造函数
		~TcpClient();//析构函数

		/**
		 * 初始化客户端
		 * ip_addr:	ipv4的地址
		 * port:	通信的端口号
		 */
		bool InitClient();

		/*
		 * 连接服务器
		 * */
		bool Connect(const string& ip_addr , const string& post);

		/*
		 * 读取缓冲区的数据
		 * */
		int Read();

		/*
		 * 写入数据
		 * str:需要发送的数据
		 * */
		void Write(const string& str);

		/*
		 * 关闭socket
		 * */
		void CloseClientSock();

};

#endif //TCPCLIENT_H
/*================================================================
*   Copyright (C) 2021 ymbLite. All rights reserved.
*   
*   文件名称:TcpClient.cpp
*   创 建 者:ymbLite
*   创建日期:2021年11月03日
*   描    述:
*
================================================================*/


#include "TcpClient.h"

in_addr GetHostByName(const string& ip_addr){
	struct hostent* host;
	host = gethostbyname(ip_addr.c_str());
	struct in_addr _sin_addr;
	memset(&_sin_addr , 0 , sizeof(_sin_addr));
	if(!host){
		return _sin_addr;
	}
	_sin_addr = (*(struct in_addr*)host->h_addr_list[0]);
	return _sin_addr;
}

TcpClient::TcpClient():clnt_sock(-1),buf_len(0){

}

TcpClient::~TcpClient(){

}

bool TcpClient::InitClient(){
	if(clnt_sock != -1){
		CloseClientSock();
	}
	clnt_sock = socket(PF_INET , SOCK_STREAM , 0);
	if(clnt_sock == -1){
		cout<<"客户端创建socket失败,socket() error!"<<endl;
		return false;
	}
	cout<<"客户端创建socket成功!"<<endl;
	return true;
}

bool TcpClient::Connect(const string& ip_addr , const string& port){
	if(clnt_sock == -1){
		cout<<"客户端没有创建socket,无法连接服务器!"<<endl;
		return false;
	}
	//开始连接服务器
	memset(&serv_addr , 0 , sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr = GetHostByName(ip_addr);
	serv_addr.sin_port = htons(atoi(port.c_str()));

	if(connect(clnt_sock , (struct sockaddr*)& serv_addr , sizeof(serv_addr)) == -1){
		CloseClientSock();
		cout<<"连接服务器失败"<<endl;
		return false;
	}
	cout<<"连接服务器成功..."<<endl;
	return true;
}

int TcpClient::Read(){
	if(clnt_sock < 0){
		cout<<"客户端未开启socket,无法读取"<<endl;
		return -1;
	}
	buf_len  = read(clnt_sock , buf , BUFF_SIZE);
	if(buf_len == 0){
		cout<<"对端已关闭socket,无法继续读取"<<endl;
		CloseClientSock();
		return buf_len;
	}
	buf[buf_len] = 0;
	cout<<"服务器说:"<<buf<<endl;
	return buf_len;
}

void TcpClient::Write(const string& str){
	if(clnt_sock < 0){
		cout<<"客户端未开启socket,写入失败!"<<endl;
	}else{
		write(clnt_sock , str.c_str() , str.size());	
	}
}

void TcpClient::CloseClientSock(){
	if(clnt_sock > 0){
		close(clnt_sock);
	}
	clnt_sock = -1;
}
/*================================================================
*   Copyright (C) 2021 ymbLite. All rights reserved.
*   
*   文件名称:main.cpp
*   创 建 者:ymbLite
*   创建日期:2021年11月03日
*   描    述:
*
================================================================*/


#include "TcpClient.h"

int main(int argc , char* argv[]){

	TcpClient tcp_client;
	bool ope_res = tcp_client.InitClient();
	if(!ope_res){
		return -1;
	}
	ope_res = tcp_client.Connect(argv[1] , argv[2]);
	if(!ope_res){
		return -2;
	}

	cout<<"开始发送数据(输入q退出)"<<endl;
	while(1){
		string str;
		cin>>str;
		if(!strcmp(str.c_str() , "q")){
			break;
		}
		tcp_client.Write(str);
		if(tcp_client.Read() <= 0){
			break;
		}
	}
	tcp_client.CloseClientSock();

	return 0;
}

服务端

/*================================================================
*   Copyright (C) 2021 ymbLite. All rights reserved.
*   
*   文件名称:TcpServer.h
*   创 建 者:ymbLite
*   创建日期:2021年11月03日
*   描    述:socket编程中,简单的TCP服务端
*
================================================================*/


#ifndef _TCPSERVER_H
#define _TCPSERVER_H

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <cstring>
#include <unistd.h>

#define BUFF_SZIE 1024

using std::cout;
using std::endl;
using std::cin;
using std::string;

class TcpServer{
	private:
		struct sockaddr_in serv_addr;//服务器中服务端的网络地址结构
		struct sockaddr_in clnt_addr;//服务端中客户端的网络地址结构
		socklen_t clnt_addr_len;//服务器中客户端的网络地址结构的长度
		char buf[BUFF_SZIE];//读写缓冲区
		int buf_len;//读取的字节长度
	public:
		int serv_sock;//服务器中服务端的socket描述符
		int clnt_sock;//服务器中客户端的socket描述符

	private:
	public:
		//构造函数
		TcpServer();
		//析构函数
		~TcpServer();

		/*
		 * 初始化服务器
		 * 服务端地址使用INADDR_ANY
		 * port:端口号
		 * */
		bool InitServer(const string& port);

		/*
		 * 阻塞接受客户端的连接
		 * */
		bool Accept();

		/**
		 * 关闭服务器中服务端的套接字
		 */
		void CloseServerSock();

		/*
		 * 关闭服务器中客户端的套接字
		 * */
		void CloseClientSock();

		/*
		 * 读取客户端的数据
		 * */
		int Read();		

		/**
		 * 回写客户端的数据
		 */
		void Write();
};
#endif //TCPSERVER_H
/*================================================================
*   Copyright (C) 2021 ymbLite. All rights reserved.
*   
*   文件名称:TcpServer.cpp
*   创 建 者:ymbLite
*   创建日期:2021年11月03日
*   描    述:
*
================================================================*/


#include "TcpServer.h"

TcpServer::TcpServer():serv_sock(-1),clnt_sock(-1){
	cout<<"TCP服务器的构造函数"<<endl;
}

TcpServer::~TcpServer(){
	cout<<"TCP服务器的析构函数"<<endl;
}

bool TcpServer::InitServer(const string& port){

	//先判断服务是否已经开启
	if(serv_sock > 0){
		CloseServerSock();
		serv_sock = -1;
	}

	//创建服务器中的服务端socket
	serv_sock = socket(PF_INET , SOCK_STREAM , 0);
	if(serv_sock == -1){
		cout<<"服务器创建监听套接字失败,socket() error!"<<endl;
		return false;
	}	

	//绑定服务端的socket到相应的网络地址结构中
	memset(&serv_addr , 0 , sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(atoi(port.c_str()));
	if(bind(serv_sock , (struct sockaddr*)& serv_addr , sizeof(serv_addr)) == -1){
		cout<<"服务器绑定套接字失败,bind() error!"<<endl;
		//关闭服务端的socket
		CloseServerSock();
		return false;

	}

	//开始监听服务端的套接字
	if(listen(serv_sock , 5) == -1){
		cout<<"服务端监听套接字失败,listen() error!"<<endl;
		//关闭服务端的socket
		CloseServerSock();
		return false;
	}

	cout<<"服务端初始化完成,开始接受客户端的连接..."<<endl;
	return true;
}

bool TcpServer::Accept(){
	//判断服务端是否已经开启了socket
	if(serv_sock == -1){
		cout<<"服务器中未开启监听的socket!"<<endl;
		return false;
	}
	//获取网络地址结构的长度
	clnt_addr_len = sizeof(clnt_addr);
	clnt_sock = accept(serv_sock , (struct sockaddr*)& clnt_addr , &clnt_addr_len);
	
	if(clnt_sock == -1){
		cout<<"服务器无法接受客户端的连接"<<endl;
		return false;
	}
	return true;
}

void TcpServer::CloseServerSock(){
	if(serv_sock > 0){
		close(serv_sock);
		serv_sock = -1;
	}
}

void TcpServer::CloseClientSock(){
	if(clnt_sock > 0){
		close(clnt_sock);
		serv_sock = -1;
	}
}

int TcpServer::Read(){
	if(clnt_sock == -1){
		cout<<"未有客户端进行连接"<<endl;
		return 0;
	}
	buf_len = read(clnt_sock , buf , BUFF_SZIE);
	if(buf_len == 0){
		//对端断开了连接,关闭对端
		CloseClientSock();
		return buf_len;
	}
	buf[buf_len] = 0;
	cout<<"【"<<clnt_sock<<"】说:"<<buf<<endl;
	return buf_len;
}

void TcpServer::Write(){
	if(clnt_sock == -1){
		cout<<"未有客户端进行连接,写入失败"<<endl;
	}else{
		write(clnt_sock , buf , buf_len);
	}
}
/*================================================================
*   Copyright (C) 2021 ymbLite. All rights reserved.
*   
*   文件名称:main.cpp
*   创 建 者:ymbLite
*   创建日期:2021年11月03日
*   描    述:
*
================================================================*/

#include "TcpServer.h"

int main(int argc ,char* argv[]){

	if(argc != 2){
		cout<<"Usage : "<<argv[0]<<" <PORT>"<<endl;
		return -1;
	}

	TcpServer tcp_server;
	//初始化服务器
	bool init_res = tcp_server.InitServer(argv[1]);
	if(!init_res){
		cout<<"服务器初始化失败"<<endl;
		return -1;
	}
	//接受客户端的连接,现在模拟只接受一个客户端的情况
	bool accept_res = tcp_server.Accept();
	if(!accept_res){
		return -2;
	}
	
	//开始回声
	while(1){
		if(tcp_server.Read() == 0){
			cout<<"客户端退出了"<<endl;
			break;
		}
		tcp_server.Write();
	}
	tcp_server.CloseServerSock();
	tcp_server.CloseClientSock();

	return 0;
}

第五:结尾

        我是把项目分成了客户端和服务端,如果初学者,可以直接复制进去,进行编译,就可以运行了,我代码必要的地方都会注释,希望能帮助到你。

很高兴能够回答你的问题。以下是一个简单的即时通信系统的C++网络编程示例,基于Linux平台使用socket库进行开发。 客户端代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main() { // 创建socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); // 配置服务器地址 sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_port = htons(8080); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接服务器 int connect_result = connect(client_socket, (sockaddr *) &server_address, sizeof(server_address)); if (connect_result == -1) { cout << "连接服务器失败!" << endl; return -1; } // 发送消息 char message[1024] = {0}; while (true) { cin.getline(message, sizeof(message)); if (strlen(message) == 0) { continue; } send(client_socket, message, strlen(message), 0); memset(message, 0, sizeof(message)); } // 关闭socket close(client_socket); return 0; } ``` 服务器端代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main() { // 创建socket int server_socket = socket(AF_INET, SOCK_STREAM, 0); // 配置服务器地址 sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_port = htons(8080); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 绑定socket和地址 bind(server_socket, (sockaddr *) &server_address, sizeof(server_address)); // 监听客户端连接 listen(server_socket, 10); // 接受客户端连接 sockaddr_in client_address{}; socklen_t client_address_len = sizeof(client_address); int client_socket = accept(server_socket, (sockaddr *) &client_address, &client_address_len); // 接收消息 char message[1024] = {0}; while (true) { int recv_result = recv(client_socket, message, sizeof(message), 0); if (recv_result <= 0) { break; } cout << "收到消息:" << message << endl; memset(message, 0, sizeof(message)); } // 关闭socket close(client_socket); close(server_socket); return 0; } ``` 在上面的代码中,客户端和服务器端都使用了socket库中的函数来完成网络通信。客户端使用`socket`函数创建socket,使用`connect`函数连接服务器,并使用`send`函数发送消息。服务器端使用`socket`函数创建socket,使用`bind`函数将socket与服务器地址绑定,使用`listen`函数监听客户端连接,并使用`accept`函数接受客户端连接,最后使用`recv`函数接收客户端发送的消息。 当然,这只是一个简单的示例。在实际开发中,你需要考虑更多的细节,例如如何处理多个客户端连接、如何处理异常情况等等。 希望这个示例能够帮助你了解LinuxC++ socket网络编程的基本流程和方法。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值