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网络编程(三)中那 ...
随机推荐
- 转发离线安装 Android Studio 更新
1.在线更新 随着 Android Studio 的越来越完善与流行,无论从功能性,还是性能上,它正在成为广大 Android 开发者的首选.但是因为总所周知墙的原因,我们在 Android Stud ...
- MyBatis入门(一)---基本使用
一.MyBatis简介 1.1.概述 MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. M ...
- IOS 网络浅析-(十三 SDWebImage 实用技巧)
IOS 网络浅析-(十三 SDWebImage 实用技巧) 首先让我描述一下为了什么而产生的实用技巧.(在TableView.CollectionView中)当用户所处环境WiFi网速不够快(不能立即 ...
- 关于OC中直接打印结构体,点(CGRect,CGSize,CGPoint,UIOffset)等数据类型
关于OC直接打印结构体,点(CGRect,CGSize,CGPoint,UIOffset)等数据类型,我们完全可以把其转换为OC对象来进项打印调试,而不必对结构体中的成员变量进行打印.就好比我们可以使 ...
- 捡火柴的Nova君(n个线段相交问题)
题目来源:https://biancheng.love/contest-ng/index.html#/41/problems 捡火柴的Nova君 题目描述 南方没暖气,怕冷的的宝宝们只能用火柴取暖.然 ...
- 解决win2003/2008下注册机或破解补丁程序无法运行问题
win Server 2003/2008 64位系统均遇到注册机或破解补丁程序无法运行或报错或死机的情况,原因是win系统默认开启了文件数据执行保护导致的. (比如3DMax的破解补丁程序等...) ...
- rails开发demo(一)搭建环境
环境 ubuntu 14.04 为了安装rails4.2 和 ruby 2.2.2,这是目前稳定的最新版本,需要先安装rvm sudo apt-get update sudo apt-get inst ...
- nginx服务器中的安全配置
一.关闭SELinux 安全增强型Linux(SELinux)的是一个Linux内核的功能,它提供支持访问控制的安全政策保护机制. 但是,SELinux带来的附加安全性和使用复杂性上不成比例,性价比不 ...
- 【同步复制常见错误处理3】找不到存储的过程 sp_MSins_tablename
环境在SQL2008 R2同步复制时出错 这个错误提示是由于在订阅端没有找到同步时调用的同步存储过程,MS错误说明: 当某个事务发布在 SQL SERVER自动同步设置选择订阅服务器插入. 更新和删除 ...
- solrcloud 配置实践
1.环境 3台虚拟机:192.168.26.129.192.168.26.131.192.168.26.132,使用命令sudo iptables -F 关闭防火墙 Solr: solr-6.1.0 ...