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)
#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)
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;
}