Linux 网络编程八(epoll应用--大并发处理)
//头文件 pub.h
#ifndef _vsucess #define _vsucess #ifdef __cplusplus
extern "C"
{ #endif
//服务器创建socket
int server_socket(int port); //设置非阻塞
int setnonblock(int st); //接收客户端socket
int server_accept(int st); //关闭socket
int close_socket(int st); //接收消息
int socket_recv(int st); //连接服务器
int connect_server(char *ipaddr,int port); //发送消息
int socket_send(int st); //将sockaddr_in转化成IP地址
int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr); #ifdef __cplusplus
}
#endif #endif
//辅助方法--pub.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函数头文件
#include <netinet/in.h>//inet_addr()头文件
#include <fcntl.h>
#include "pub.h" #define MAXBUF 1024 //创建socket
int socket_create()
{
int st = socket(AF_INET, SOCK_STREAM, );
if (st == -)
{
printf("create socket failed ! error message :%s\n", strerror(errno));
return -;
}
return st;
} //设置服务端socket地址重用
int socket_reuseaddr(int st)
{
int on = ;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -)
{
printf("setsockopt reuseaddr failed ! error message :%s\n",
strerror(errno));
//close socket
close_socket(st);
return -;
}
return ;
} //服务器绑定--监听端口号
int socket_bind(int st, int port)
{
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
//type
addr.sin_family = AF_INET;
//port
addr.sin_port = htons(port);
//ip
addr.sin_addr.s_addr = htonl(INADDR_ANY);
//bind ip address
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -)
{
printf("bind failed ! error message :%s\n", strerror(errno));
//close socket
close_socket(st);
return -;
}
//listen
if (listen(st, ) == -)
{
printf("listen failed ! error message :%s\n", strerror(errno));
//close socket
close_socket(st);
return -;
}
return ;
} //服务器创建socket
int server_socket(int port)
{
if (port < )
{
printf("function server_socket param not correct !\n");
return -;
}
//create socket
int st = socket_create();
if (st < )
{
return -;
}
//reuseaddr
if (socket_reuseaddr(st) < )
{
return -;
}
//bind and listen
if (socket_bind(st, port) < )
{
return -;
}
return st;
} //连接服务器
int connect_server(char *ipaddr,int port)
{
if(port<||ipaddr==NULL)
{
printf("function connect_server param not correct !\n");
return -;
}
int st=socket_create();
if(st<)
{
return -;
}
//conect server
struct sockaddr_in addr;
memset(&addr,,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=inet_addr(ipaddr);
if(connect(st,(struct sockaddr *)&addr,sizeof(addr))==-)
{
printf("connect failed ! error message :%s\n",strerror(errno));
return -;
}
return st;
} //设置非阻塞
int setnonblock(int st)
{
if (st < )
{
printf("function setnonblock param not correct !\n");
//close socket
close_socket(st);
return -;
}
int opts = fcntl(st, F_GETFL);
if (opts < )
{
printf("func fcntl failed ! error message :%s\n", strerror(errno));
return -;
}
opts = opts | O_NONBLOCK;
if (fcntl(st, F_SETFL, opts) < )
{
printf("func fcntl failed ! error message :%s\n", strerror(errno));
return -;
}
return opts;
} //接收客户端socket
int server_accept(int st)
{
if (st < )
{
printf("function accept_clientsocket param not correct !\n");
return -;
}
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
socklen_t len = sizeof(addr);
int client_st = accept(st, (struct sockaddr *) &addr, &len);
if (client_st < )
{
printf("accept client failed ! error message :%s\n", strerror(errno));
return -;
} else
{
char ipaddr[] = { };
sockaddr_toa(&addr, ipaddr);
printf("accept by %s\n", ipaddr);
}
return client_st;
} //关闭socket
int close_socket(int st)
{
if (st < )
{
printf("function close_socket param not correct !\n");
return -;
}
close(st);
return ;
} //将sockaddr_in转化成IP地址
int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr)
{
if (addr == NULL || ipaddr == NULL)
{
return -;
}
unsigned char *p = (unsigned char *) &(addr->sin_addr.s_addr);
sprintf(ipaddr, "%u.%u.%u.%u", p[], p[], p[], p[]);
return ;
} //接收消息
int socket_recv(int st)
{
if (st < )
{
printf("function socket_recv param not correct !\n");
return -;
}
char buf[MAXBUF] = { };
int rc=;
rc=recv(st,buf,sizeof(buf),);
if(rc==)
{
printf("client is close ! \n");
return -;
}else if(rc<)
{
/*
* recv错误信息:Connection reset by peer
* 错误原因:服务端给客户端发送数据,但是客户端没有接收,直接关闭,那么就会报错
* 如果客户端接受了数据,再关闭,也不会报错,rc==0.
*/
printf("recv failed ! error message :%s \n",strerror(errno));
return -;
}
printf("%s",buf);
//send message
/*
memset(buf,0,sizeof(buf));
strcpy(buf,"i am server , i have recved !\n");
if(send(st,buf,strlen(buf),0)<0)
{
printf("send failed ! error message :%s \n",strerror(errno));
return -1;
}
*/
return ;
} //发送消息
int socket_send(int st)
{
char buf[MAXBUF]={};
while()
{
//read from keyboard
read(STDIN_FILENO,buf,sizeof(buf));
if(buf[]=='')
{
break;
}
if(send(st,buf,strlen(buf),)<)
{
printf("send failed ! error message :%s \n",strerror(errno));
return -;
}
memset(buf,,sizeof(buf));
}
return ;
}
//网络编程服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函数头文件
#include <netinet/in.h>//inet_addr()头文件
#include <fcntl.h>
#include <sys/epoll.h>
#include "pub.h" #define MAXSOCKET 20 int main(int arg, char *args[])
{
if (arg < )
{
printf("please print one param!\n");
return -;
}
//create server socket
int listen_st = server_socket(atoi(args[]));
if (listen_st < )
{
return -;
}
/*
* 声明epoll_event结构体变量ev,变量ev用于注册事件,
* 数组events用于回传需要处理的事件
*/
struct epoll_event ev, events[];
//生成用于处理accept的epoll专用文件描述符
int epfd = epoll_create(MAXSOCKET);
//把socket设置成非阻塞方式
setnonblock(listen_st);
//设置需要放到epoll池里的文件描述符
ev.data.fd = listen_st;
//设置这个文件描述符需要epoll监控的事件
/*
* EPOLLIN代表文件描述符读事件
*accept,recv都是读事件
*/
ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
/*
* 注册epoll事件
* 函数epoll_ctl中&ev参数表示需要epoll监视的listen_st这个socket中的一些事件
*/
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_st, &ev); while ()
{
/*
* 等待epoll池中的socket发生事件,这里一般设置为阻塞的
* events这个参数的类型是epoll_event类型的数组
* 如果epoll池中的一个或者多个socket发生事件,
* epoll_wait就会返回,参数events中存放了发生事件的socket和这个socket所发生的事件
* 这里强调一点,epoll池存放的是一个个socket,不是一个个socket事件
* 一个socket可能有多个事件,epoll_wait返回的是有消息的socket的数目
* 如果epoll_wait返回事件数组后,下面的程序代码却没有处理当前socket发生的事件
* 那么epoll_wait将不会再次阻塞,而是直接返回,参数events里面的就是刚才那个socket没有被处理的事件
*/
int nfds = epoll_wait(epfd, events, MAXSOCKET, -);
if (nfds == -)
{
printf("epoll_wait failed ! error message :%s \n", strerror(errno));
break;
}
int i = ;
for (; i < nfds; i++)
{
if (events[i].data.fd < )
continue;
if (events[i].data.fd == listen_st)
{
//接收客户端socket
int client_st = server_accept(listen_st);
/*
* 监测到一个用户的socket连接到服务器listen_st绑定的端口
*
*/
if (client_st < )
{
continue;
}
//设置客户端socket非阻塞
setnonblock(client_st);
//将客户端socket加入到epoll池中
struct epoll_event client_ev;
client_ev.data.fd = client_st;
client_ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
epoll_ctl(epfd, EPOLL_CTL_ADD, client_st, &client_ev);
/*
* 注释:当epoll池中listen_st这个服务器socket有消息的时候
* 只可能是来自客户端的连接消息
* recv,send使用的都是客户端的socket,不会向listen_st发送消息的
*/
continue;
}
//客户端有事件到达
if (events[i].events & EPOLLIN)
{
//表示服务器这边的client_st接收到消息
if (socket_recv(events[i].data.fd) < )
{
close_socket(events[i].data.fd);
//接收数据出错或者客户端已经关闭
events[i].data.fd = -;
/*这里continue是因为客户端socket已经被关闭了,
* 但是这个socket可能还有其他的事件,会继续执行其他的事件,
* 但是这个socket已经被设置成-1
* 所以后面的close_socket()函数都会报错
*/
continue;
}
/*
* 此处不能continue,因为每个socket都可能有多个事件同时发送到服务器端
* 这也是下面语句用if而不是if-else的原因,
*/ }
//客户端有事件到达
if (events[i].events & EPOLLERR)
{
printf("EPOLLERR\n");
//返回出错事件,关闭socket,清理epoll池,当关闭socket并且events[i].data.fd=-1,epoll会自动将该socket从池中清除
close_socket(events[i].data.fd);
events[i].data.fd = -;
continue;
}
//客户端有事件到达
if (events[i].events & EPOLLHUP)
{
printf("EPOLLHUP\n");
//返回挂起事件,关闭socket,清理epoll池
close_socket(events[i].data.fd);
events[i].data.fd = -;
continue;
}
}
}
//close epoll
close(epfd);
//close server socket
close_socket(listen_st);
return ;
}
//网络编程客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函数头文件
#include <netinet/in.h>//inet_addr()头文件
#include <fcntl.h>
#include <sys/epoll.h>
#include "pub.h" int main(int arg,char *args[])
{
if(arg<)
{
printf("please print two param !\n");
}
//端口号
int port=atoi(args[]);
//服务端IP地址
char ipaddr[]={};
strcpy(ipaddr,args[]);
//connect server
int st=connect_server(ipaddr,port);
//send message
//发送消息--
socket_send(st);
//close socket
close(st);
return ;
}
.SUFFIXES:.c .o
CC=gcc
SRCS1=epoll_client.c\
pub.c
SRCS2=epoll_server.c\
pub.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=mclient
EXEC2=mserver start:$(OBJS1) $(OBJS2)
$(CC) -o $(EXEC1) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2)
@echo "-------ok-----------"
.c.o:
$(CC) -Wall -g -o $@ -c $<
clean:
rm -f $(OBJS1)
rm -f $(EXEC1)
rm -f $(OBJS2)
rm -f $(EXEC2)
Linux 网络编程八(epoll应用--大并发处理)的更多相关文章
- Linux 网络编程(epoll)
服务器端代码 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/soc ...
- Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)
Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...
- 【深入浅出Linux网络编程】 "开篇 -- 知其然,知其所以然"
[深入浅出Linux网络编程]是一个连载博客,内容源于本人的工作经验,旨在给读者提供靠谱高效的学习途径,不必在零散的互联网资源中浪费精力,快速的掌握Linux网络编程. 连载包含4篇,会陆续编写发出, ...
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- [转] - Linux网络编程 -- 网络知识介绍
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- 【转】Linux网络编程入门
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- 《转》Linux网络编程入门
原地址:http://www.cnblogs.com/duzouzhe/archive/2009/06/19/1506699.html (一)Linux网络编程--网络知识介绍 Linux网络编程-- ...
- Linux网络编程(六)
网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...
- Linux网络编程(四)
在linux网络编程[1-3]中,我们编写的网络程序仅仅是为了了解网络编程的基本步骤,实际应用当中的网络程序并不会用那样的.首先,如果服务器需要处理高并发访问,通常不会使用linux网络编程(三)中那 ...
随机推荐
- Eclipse环境下配置spket中ExtJS5.0提示
使用eclipse编写extjs时,一定会用到spket这个插件,spket可以单独当作ide使用,也可以当作eclipse插件使用,我这里是当作eclipse的插件使用的,下面来一步步图解说明如何配 ...
- UIViewController的edgesForExtendedLayout属性
UIViewController的edgesForExtendedLayout属性 想必大家都遇到一种情况,明明y坐标设置的是0,但是总是被讨厌的导航栏给遮住.比如下面这个情况: UILabel *l ...
- Android中ListView 控件与 Adapter 适配器如何使用?
一个android应用的成功与否,其界面设计至关重要.为了更好的进行android ui设计,我们常常需要借助一些控件和适配器.今天小编在android培训网站上搜罗了一些有关ListView 控件与 ...
- HashMap总结
最近朋友推荐的一个很好的工作,又是面了2轮没通过,已经是好几次朋友内推没过了,觉得挺对不住朋友的.面试反馈有一方面是有些方面理解思考的还不够,平时也是项目进度比较紧,有些方面赶进度时没有理解清楚的后面 ...
- Android开发艺术探索学习笔记(三)
第三章 View的事件体系 3.1 View基础知识 3.1.1 什么是view View 是Android中所有控件的基类,是一种界面层的控件的一种抽象,它代表了一个控件. 3.1.2 View的 ...
- Java 图片处理——如何生成高清晰度而占有磁盘小的缩略图
现在的web项目,图片越来越多,图片大小也越来越大,随便就能达到1M,2M,甚至更大.用户上传的图片,一般是无法直接使用的.一般要生成两三种对应的缩略图,分别适配不同的终端,不同的场景.比如PC,手机 ...
- device eth0 does not seem to be present, delaying initialization
在搭建LVS+Keepalived高可用负载均衡环境的过程中,使用VirtualBox复制了两个Centos的环境,并且选中了“重新初始化网卡”的选项,但是在启动这两个复制的Centos环境的时候,发 ...
- 关于Redis中的serverCron
1.serverCron简介 在 Redis 中, 常规操作由 redis.c/serverCron 实现, 它主要执行以下操作 /* This is our timer interrupt, cal ...
- centos7 新手基本命令
1. yum update 安装系统后,更新yum到最新版本 提示错误 :cannot find a valid baseurl for repo: base/7/x86_64 解决:修改/etc/s ...
- JAVA基础知识点:内存、比较和Final
1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题.(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对 ...