【后端开发】echo server

开发部分:直接上代码(服务端)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc,char* argv[])
{
	if (argc <= 2)
	{
		printf( "Usage: %s ip_address portname\n", argv[0] );
		return 0;
	}

	const char* ip = argv[1];
	int port = atoi( argv[2] );
    
	int listenfd = socket( PF_INET, SOCK_STREAM, 0 );//一、创建socket文件描述符
	assert( listenfd >= 1 );

	struct sockaddr_in address;//socket地址,封装了端口号和ip地址(IPv4)
	memset( &address, 0, sizeof( address ) );		//address清零
	address.sin_family = AF_INET;					//设置address的地址族
	address.sin_port = htons( port );				//设置address的端口号
	inet_pton( AF_INET, ip, &address.sin_addr );	//设置address的ip地址

	int ret = 0;
	ret = bind( listenfd, (struct sockaddr*)( &address ), 
				sizeof( address ) );	//二、绑定socket与socket地址
	assert( ret != -1 );

	ret = listen( listenfd, 5 );		//三、创建监听队列(以存放待处理的客户连接),指定listenfd为被监听socket
	assert( ret != -1 );
	
	struct sockaddr_in client;//存放将被接受的客户socket地址
	socklen_t client_addrlength = sizeof( client );
	int sockfd = accept( listenfd, (struct sockaddr*)( &client ), &client_addrlength );//四、从监听队列里接受一个连接,返回标识被接受的连接的socket,用于后续通信,存远端socket地址到client
				   	  	 
	char buf_size[1024] = {0};
	int recv_size = 0;
	recv_size = recv( sockfd, buf_size, sizeof( buf_size ) , 0);//TCP数据读,返回读取数据的长度
	
	int send_size = 0;
	send_size = send( sockfd, buf_size , recv_size , 0 );//TCP数据写,返回读取数据的长度
	
	close( sockfd );	//五、关闭连接
	close( listenfd );  //五、关闭监听socket

	return 0;
}

测试部分
服务端:
在这里插入图片描述
客户端(nc模拟):
在这里插入图片描述
在这里插入图片描述
很明显上述echo服务器只能处理一次客户连接

IO复用版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>

#define MAX_EVENTS_NUMBER 5

//将fd设置为非阻塞的,并返回旧状态
int set_non_blocking( int fd )
{
	int old_state = fcntl( fd, F_GETFL );
	int new_state = old_state | O_NONBLOCK;
	fcntl( fd, F_SETFL, new_state );

	return old_state;	
}

//将fd上的EPOLLIN注册到epollfd指示的epoll内核事件表中,并启用EPOLL ET模式,并将fd设置为非阻塞模式
void addfd( int epollfd , int fd )
{
	epoll_event event;
	event.events = EPOLLIN | EPOLLET;//指定事件
    event.data.fd = fd;
	epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
	set_non_blocking( fd );	
}


int main( int argc , char* argv[] )
{
	if (argc <= 2)
	{
		printf( "Usage: %s ip_address portname\n", argv[0] );
		return 0;
	}

	const char* ip = argv[1];
	int port = atoi( argv[2] );
    
	int listenfd = socket( PF_INET, SOCK_STREAM, 0 );	//一、创建socket文件描述符
	assert( listenfd >= 1 );

	struct sockaddr_in address;//socket地址,封装了端口号和ip地址(IPv4)
	memset( &address, 0, sizeof( address ) );//address清零
	address.sin_family = AF_INET;					//设置address的地址族
	address.sin_port = htons( port );				//设置address的端口号
	inet_pton( AF_INET, ip, &address.sin_addr );	//设置address的ip地址

	int ret = 0;
	ret = bind( listenfd, (struct sockaddr*)( &address ), 
				sizeof( address ) );		//二、绑定socket与socket地址
	assert( ret != -1 );

	ret = listen( listenfd, 5 );			//三、创建监听队列(以存放待处理的客户连接),指定listenfd为被监听socket
	assert( ret != -1 );
	
	epoll_event events[ MAX_EVENTS_NUMBER ];//存放就绪事件的数组
    int epollfd = epoll_create( 5 );		//四、创建文件描述符,来标识内核中的事件表
	assert( epollfd != -1);
	addfd( epollfd, listenfd );				//五、注册listenfd到事件表中,监听其可读事件(对其可读事件进行处理)

	while(1)								//六、以事件为驱动,处理各类可读事件(包括接受新连接)
	{
		int number = epoll_wait( epollfd, events, MAX_EVENTS_NUMBER, -1 );//返回就绪fd个数
		if( number < 0 )
		{
			printf( "epoll_wait failed\n" );
			return -1;
		}

		for( int i = 0; i < number; ++i )
		{
			const auto& event = events[i];
			const auto eventfd = event.data.fd;

			if( eventfd == listenfd )	//6.1 说明监听队列里有新客户连接
			{							//接受新连接,并将新连接注册到事件表中
				struct sockaddr_in client;
				socklen_t client_addrlength = sizeof( client );
				int sockfd = accept( listenfd, ( struct sockaddr* )( &address ),
							   	     &client_addrlength );					
				addfd( epollfd, sockfd );//将新连接注册到事件表中,监听其可读事件	 
			}
			else if( event.events & EPOLLIN ) //6.2 处理客户连接的可读事件
			{
				char buf[1024] = {0};
				while(1)
				{
					memset( buf, '\0', sizeof( buf ) );
					int recv_size  = recv( eventfd, buf, sizeof( buf ), 0 );//TCP数据读,返回读取数据的长度
					if( recv_size < 0 )//错误处理
					{
						if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) )
						{
							break;
						}
						printf(" sockfd %d,recv msg failed\n", eventfd );
						close( eventfd );
						break;
					}
					else if( recv_size == 0)
					{
					   	close( eventfd );
						break;	
					}
					else
					{
						send( eventfd, buf, recv_size, 0 );//TCP数据写
					}	
				}
			}
		}	
	}

	close( listenfd );//五、关闭监听socket

	return 0;
}

测试部分
服务端:
在这里插入图片描述
2个客户端(nc模拟):
在这里插入图片描述
在这里插入图片描述

### 关于C++后端开发项目的资源与教程 对于希望深入了解并实践C++后端开发的开发者来说,选择合适的项目示例和教程至关重要。以下是几个推荐的方向以及相关内容: #### 高质量学习资源的选择 为了更高效地掌握C++后端开发技能,可以选择一些经典书籍作为理论基础,比如《C++ Primer》或《C++ Primer Plus》,这些书能够提供扎实的语言基础知识[^1]。 除了阅读教材外,参与实际项目也是必不可少的一环。可以通过查找开源社区中的C++后端项目获取灵感或者直接贡献代码。GitHub是一个很好的起点,在这里可以找到许多基于不同框架和技术栈实现的服务端解决方案。 #### 技术选型指导 了解常见的技术组合有助于规划个人或团队的工作流程。例如,使用Boost库处理复杂数据结构;采用ZeroMQ这样的轻量级消息传递机制完成异步任务调度;借助MySQL/PostgreSQL存储持久化信息等等[^2]。 另外值得注意的是,随着云计算平台日益普及,熟悉Docker容器部署方式及其编排工具Kubernetes也变得越来越重要。它们可以让应用程序更加灵活可靠地运行在分布式环境中。 #### 类与对象的理解 深入研究面向对象编程(OOP)原则是成为合格软件工程师的关键之一。在C++里,“类(class)”定义了一组具有相同属性和操作的对象(object),而后者则是前者具体存在的形式。通过创建合理的继承关系、封装敏感字段等方式提高系统的可维护性和扩展性[^3]。 #### 网络编程入门指南 由于大多数现代互联网服务都需要支持远程客户端连接请求,因此掌握基本TCP/IP模型及相关高层协议(如HTTP(s))显得尤为重要。初学者可以从简单的回声服务器做起,逐渐过渡到更为复杂的RESTful API设计模式上[^4]。 ```cpp #include <iostream> #include <boost/asio.hpp> using boost::asio::ip::tcp; int main() { try { boost::asio::io_context io_context; tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 8080)); while (true){ std::cout << "Waiting for new connection..." << std::endl; tcp::socket socket(io_context); acceptor.accept(socket); char data[1024]; size_t length = socket.read_some(boost::asio::buffer(data), ec); if (!ec){ std::string message(data,length); std::cout<<"Received:"<<message<<std::endl; // Echo back the received content. boost::system::error_code ignored_ec; write(socket,message.c_str(),ignored_ec); } } } catch(std::exception& e){ std::cerr <<e.what()<< "\n"; } return 0; } ``` 上述代码片段展示了一个非常基础版本的单线程Echo Server实现过程,它依赖第三方头文件`<boost/asio.hpp>`来进行同步套接字通信管理。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值