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;
}
相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展