【后端开发】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模拟):
在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值