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网络编程(三)中那 ...
随机推荐
- iOS第三方类库JSPatch(热更新)
---------------------------------------------------------------------------------------------------- ...
- unity下载文件二(http同步下载)
说到下载,其实C#里的网络模块,真的是被各种封装,最终就看你对这个语言中库的熟悉程度了. 抛开C#中IO效率的弊病不说,真的很容易,记住,太过于注重效率或者追求极致,你将会死的很惨,有时候折中才是最好 ...
- HTML列表元素
HTML定义了3类列表: 1.有序列表(通常用数字编号) 2.无序列表(通常前面加原点) 3.自定义列表(列表项目,带有集成的定义) 有序列表和无序列表均为列表中的每一项使用列表项元素(<li& ...
- eclipse智能提示
原文地址:http://www.cnblogs.com/myitm/archive/2010/12/17/1909194.html Windows→Preferences→Java→Editor→Co ...
- .NET 创建Windows服务,及服务的安装卸载
.NET服务创建过程 http://jingyan.baidu.com/article/fa4125acb71a8628ac709226.html 相关命令(要以管理员身份打开cmd) 安装服务 -& ...
- oracle中的数值函数整理
主要分为三块介绍(单值函数.聚合函数.列表函数) 一.单值函数(比较简单,看一遍基本也就理解记住了) 1.基本加减乘车没有什么可说的,只需要注意一点,任何值与null一起运算 ,结果都为null,因为 ...
- Linux date命令的用法
在linux shell编程中,经常用到日期的加减运算以前都是自己通过expr函数计算,很麻烦.其实date命令本身提供了日期的加减运算非常方便. 例如:得到昨天的时间date --date=&qu ...
- CentOS下SSH无密码登录的配置
1.确认本机sshd的配置文件(需要root权限) $ gedit /etc/ssh/sshd_config 找到以下内容,并去掉注释符"#" RSAAuthentication ...
- js 操作select和option
js 操作select和option 1.动态创建select function createSelect(){ var mySelect = document.createElement_x(&qu ...
- c# App.Config详解
c# App.Config详解 应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序. 配置文件的根 ...