Linux-c TCP服务模型

1、TCP模型,服务端与客户端的搭建时序图

2、TCP模型,在创建阶段和通信阶段,对套接字的理解

2.1、tcp连接阶段

2.2、tcp通信状态

一个服务端与多个客户端的通信状态

TCP与UDP的对比

(下图是笔者理解所画,可能也许有错,欢迎指出)

3、基于EPOLL多路复用模型,代码实现的TCP服务端和客户端

3.1、EPOLL的几个主要的函数

3.1.1、epoll_create1(int flags)

创建epfd,笔者理解,这个是管理众多事件的一个集合。

3.1.2、epoll_ctl

用于添加和删除fd及其关联的事件

3.1.3、epoll_wait

阻塞等待监听的fd的时间发生变化

3.2、服务端代码(TCP+EPOLL)

cpp 复制代码
#define BACKLOG 50  //支持多少个客户端连接

int main(int argc, const char *argv[])
{
	//socket
	int server=socket(AF_INET, SOCK_STREAM, 0);
	if(server == -1){
		perror("socket error");
		return -1;
	}
	
	//bind
	struct sockaddr_in serverAddr;
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=htons(atoi(argv[1]));
	serverAddr.sin_addr.s_addr=inet_addr("0.0.0.0");
	
	if(bind(server,(struct sockaddr*)&serverAddr, sizeof(serverAddr))==-1){
		perror("bind server error");
		return -1;
	}
	
	//listen
	if(listen(server, BACKLOG)==-1){
		perror("listen error");
		return -1;
	}
	
	//epoll_create
	int epfd=epoll_create1(EPOLL_CLOEXEC);
	if(epfd==-1){
		perror("epoll_create1 error");
		return -1;
	}
	
	//epoll_ctl add/del
	struct epoll_event e1;
	e1.data.fd=server;
	e1.events=EPOLLIN;
	if(epoll_ctl(epfd, EPOLL_CTL_ADD, server, &e1)==-1){//server文件FD
		perror("epoll_ctl add 1: error");
		return -1;
	}

	struct epoll_event e2;
	e2.data.fd=0;
	e2.events=EPOLLIN;
	if(epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &e2)==-1){//标准输入
		perror("epoll_ctl add 2:error");
		return -1;
	}
	
	while(1){
		//epoll_wait
		struct epoll_event eArr[50];
		int num=epoll_wait(epfd, eArr,50,-1);
		//printf("num=[%d]\n", num);
		//if(num >= 2){
		//	printf("num=[%d]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", num);
		//}
		if(num == -1){
			perror("epoll_wait error");
			return -1;
		}
		for(int i=0;i<num;i++){
			int fd=eArr[i].data.fd;
			int events=eArr[i].events;
			//以下由于只监听了EPOLLIN,因此不对事件进行判断
			if(fd==server){//服务端监听套接字
				struct sockaddr_in clientAddr;
				socklen_t clientAddrLen;
				int fd=accept(server, (struct sockaddr*)&clientAddr, &clientAddrLen);
				printf("有新的客户端连接[%s]:[%d],FD=[%d]\n",
						inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port),fd);
				//将此客户端fd添加到epfd中
				struct epoll_event e3;
				e3.data.fd=fd;
				e3.events=EPOLLIN;
				if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd, &e3)==-1){
					perror("epoll_ctl add 3 error");
					return -1;
				}
				continue;
			}

			if(fd==0){//标准输入流
				printf("请输入:");
				char buf[64];
				fgets(buf,sizeof(buf), stdin);
				buf[strlen(buf)-1]=0;
				printf("键盘键入了:%s\n", buf);
				continue;
			}

			//客户端
			char buf[64]={0};
			struct sockaddr_in clientAddr;
			socklen_t clientAddrLen;
			ssize_t cnt=recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
			printf("<<收到来自[%s]:[%d]的消息:\n[%ld]:%s\n",
					inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port),cnt,buf);
			if(cnt == 0){
				printf("<<客户端[%s]:[%d]的断开了连接:\n",
					inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
				//从epfd中删除这个fd
				epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
				continue;
				
			}
			//TODO 转换成大写,返还给客户端
			for(int j=0;j<cnt;j++){
				buf[j]=toupper(buf[j]);
			}
			cnt=send(fd, buf, sizeof(buf), 0);
			printf(">>向[%s]:[%d]发送了消息:\n[%ld]:%s\n",
					inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port),cnt,buf);

		}
	}

	return 0;
}

3.3、客户端代码(TCP+EPOLL)

cpp 复制代码
int main(int argc, const char *argv[])
{
	//tcp epoll客户端
	//socket
	int client=socket(AF_INET, SOCK_STREAM, 0);
	if(client == -1){
		perror("socket error");
		return -1;
	}
	//connect
	struct sockaddr_in serverAddr;
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=htons(atoi(argv[1]));
	serverAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
	if(connect(client, (struct sockaddr*)&serverAddr,sizeof(serverAddr))==-1){
		perror("connect error");
		return -1;
	}
	printf("与服务端成功创建连接\n");
	
	//epoll create
	int epfd=epoll_create1(EPOLL_CLOEXEC);
	if(epfd == -1){
		perror("epoll_create1 error");
		return -1;
	}

	//epoll ctl add
	struct epoll_event e1;
	e1.data.fd=client;//将客户端fd,收到消息事件,添加
	e1.events=EPOLLIN;
	if(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &e1)==-1){
		perror("epoll_ctl error [1]");
		return -1;
	}
	//添加事件:键盘输入
	//struct epoll_event e1;
	e1.data.fd=0;//将客户端fd,收到消息事件,添加
	e1.events=EPOLLIN;
	if(epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &e1)==-1){
		perror("epoll_ctl error [1]");
		return -1;
	}
	
	while(1){
		struct epoll_event eArr[2];
		int num=epoll_wait(epfd, eArr, 2, -1);
		if(num == -1){
			perror("epoll_wait error");
			return -1;
		}
		for(int i=0;i<num;i++){
			int fd=eArr[i].data.fd;
			int es=eArr[i].events;
			printf("fd=[%d],es=[%d]\n", fd, es);
			if(fd == 0){//键盘输入
				char buf[64]={0};
				fgets(buf, sizeof(buf), stdin);
				buf[strlen(buf)-1]=0;
				printf("键盘输入了:%s\n", buf);
				ssize_t cnt=send(client, buf,strlen(buf),0);
				printf("发送了[%ld]:%s\n", cnt, buf);
				if(cnt == -1){
					perror("send error");
				}
				continue;
			}
			if(fd == client){//收到消息
				char buf[64]={0};
				ssize_t cnt=recv(fd, buf, sizeof(buf), 0);
				printf("收到[%ld]:%s\n", cnt, buf);
				continue;
			}
			//不知道
			fprintf(stderr, "错误的监听FD[%d]\n", fd);
		}
	}
	
	
	//epoll ctl del
	
	//epoll wait

	
	return 0;
}
相关推荐
偶像你挑的噻12 分钟前
12-Linux驱动开发- SPI子系统
linux·驱动开发·stm32·嵌入式硬件
羑悻的小杀马特34 分钟前
轻量跨云·掌控无界:Portainer CE + cpolar 让远程容器运维像点外卖一样简单——免复杂配置,安全直达对应集群
运维·网络·安全·docker·cpolar
松涛和鸣37 分钟前
16、C 语言高级指针与结构体
linux·c语言·开发语言·数据结构·git·算法
念风1 小时前
[lvgl]如何优雅地向lv_port_linux中添加tslib支持
linux
愚戏师1 小时前
Python3 Socket 网络编程复习笔记
网络·笔记
降临-max2 小时前
JavaSE---网络编程
java·开发语言·网络·笔记·学习
156082072192 小时前
基于7VX690T FPGA实现万兆TCP/IP资源和性能测试
网络协议·tcp/ip·fpga开发
自由的好好干活2 小时前
使用Qoder编写ztdaq的C#跨平台示例总结
linux·windows·c#·qoder
赖small强2 小时前
【Linux 网络基础】libwebsockets HTTPS 服务端实现机制详解
linux·网络·https·tls·libwebsockets
大白的编程日记.3 小时前
【计算网络学习笔记】MySql的多版本控制MVCC和Read View
网络·笔记·学习·mysql