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, 0);
    if (st == -1)
    {
        printf("create socket failed ! error message :%s\n", strerror(errno));
        return -1;
    }
    return st;
}

//设置服务端socket地址重用
int socket_reuseaddr(int st)
{
    int on = 1;
    if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsockopt reuseaddr failed ! error message :%s\n",
                strerror(errno));
        //close socket
        close_socket(st);
        return -1;
    }
    return 0;
}

//服务器绑定--监听端口号
int socket_bind(int st, int port)
{
    struct sockaddr_in addr;
    memset(&addr, 0, 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)) == -1)
    {
        printf("bind failed ! error message :%s\n", strerror(errno));
        //close socket
        close_socket(st);
        return -1;
    }
    //listen
    if (listen(st, 20) == -1)
    {
        printf("listen failed ! error message :%s\n", strerror(errno));
        //close socket
        close_socket(st);
        return -1;
    }
    return 0;
}

//服务器创建socket
int server_socket(int port)
{
    if (port < 0)
    {
        printf("function server_socket param not correct !\n");
        return -1;
    }
    //create socket
    int st = socket_create();
    if (st < 0)
    {
        return -1;
    }
    //reuseaddr
    if (socket_reuseaddr(st) < 0)
    {
        return -1;
    }
    //bind and listen
    if (socket_bind(st, port) < 0)
    {
        return -1;
    }
    return st;
}

//连接服务器
int connect_server(char *ipaddr,int port)
{
    if(port<0||ipaddr==NULL)
    {
        printf("function connect_server param not correct !\n");
        return -1;
    }
    int st=socket_create();
    if(st<0)
    {
        return -1;
    }
    //conect server
    struct sockaddr_in addr;
    memset(&addr,0,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))==-1)
    {
        printf("connect failed ! error message :%s\n",strerror(errno));
        return -1;
    }
    return st;
}

//设置非阻塞
int setnonblock(int st)
{
    if (st < 0)
    {
        printf("function setnonblock param not correct !\n");
        //close socket
        close_socket(st);
        return -1;
    }
    int opts = fcntl(st, F_GETFL);
    if (opts < 0)
    {
        printf("func fcntl failed ! error message :%s\n", strerror(errno));
        return -1;
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(st, F_SETFL, opts) < 0)
    {
        printf("func fcntl failed ! error message :%s\n", strerror(errno));
        return -1;
    }
    return opts;
}

//接收客户端socket
int server_accept(int st)
{
    if (st < 0)
    {
        printf("function accept_clientsocket param not correct !\n");
        return -1;
    }
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    socklen_t len = sizeof(addr);
    int client_st = accept(st, (struct sockaddr *) &addr, &len);
    if (client_st < 0)
    {
        printf("accept client failed ! error message :%s\n", strerror(errno));
        return -1;
    } else
    {
        char ipaddr[20] = { 0 };
        sockaddr_toa(&addr, ipaddr);
        printf("accept by %s\n", ipaddr);
    }
    return client_st;
}

//关闭socket
int close_socket(int st)
{
    if (st < 0)
    {
        printf("function close_socket param not correct !\n");
        return -1;
    }
    close(st);
    return 0;
}

//将sockaddr_in转化成IP地址
int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr)
{
    if (addr == NULL || ipaddr == NULL)
    {
        return -1;
    }
    unsigned char *p = (unsigned char *) &(addr->sin_addr.s_addr);
    sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
    return 0;
}

//接收消息
int socket_recv(int st)
{
    if (st < 0)
    {
        printf("function socket_recv param not correct !\n");
        return -1;
    }
    char buf[MAXBUF] = { 0 };
    int rc=0;
    rc=recv(st,buf,sizeof(buf),0);
    if(rc==0)
    {
        printf("client is close ! \n");
        return -1;
    }else if(rc<0)
    {
        /*
         * recv错误信息:Connection reset by peer
         * 错误原因:服务端给客户端发送数据,但是客户端没有接收,直接关闭,那么就会报错
         * 如果客户端接受了数据,再关闭,也不会报错,rc==0.
         */
        printf("recv failed ! error message :%s \n",strerror(errno));
        return -1;
    }
    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 0;
}

//发送消息
int socket_send(int st)
{
    char buf[MAXBUF]={0};
    while(1)
    {
        //read from keyboard
        read(STDIN_FILENO,buf,sizeof(buf));
        if(buf[0]==‘0‘)
        {
            break;
        }
        if(send(st,buf,strlen(buf),0)<0)
        {
            printf("send failed ! error message :%s \n",strerror(errno));
            return -1;
        }
        memset(buf,0,sizeof(buf));
    }
    return 0;
}
//网络编程服务端
#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 < 2)
    {
        printf("please print one param!\n");
        return -1;
    }
    //create server socket
    int listen_st = server_socket(atoi(args[1]));
    if (listen_st < 0)
    {
        return -1;
    }
    /*
     * 声明epoll_event结构体变量ev,变量ev用于注册事件,
     * 数组events用于回传需要处理的事件
     */
    struct epoll_event ev, events[100];
    //生成用于处理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 (1)
    {
        /*
         * 等待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, -1);
        if (nfds == -1)
        {
            printf("epoll_wait failed ! error message :%s \n", strerror(errno));
            break;
        }
        int i = 0;
        for (; i < nfds; i++)
        {
            if (events[i].data.fd < 0)
                continue;
            if (events[i].data.fd == listen_st)
            {
                //接收客户端socket
                int client_st = server_accept(listen_st);
                /*
                 * 监测到一个用户的socket连接到服务器listen_st绑定的端口
                 *
                 */
                if (client_st < 0)
                {
                    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) < 0)
                {
                    close_socket(events[i].data.fd);
                    //接收数据出错或者客户端已经关闭
                    events[i].data.fd = -1;
                    /*这里continue是因为客户端socket已经被关闭了,
                     * 但是这个socket可能还有其他的事件,会继续执行其他的事件,
                     * 但是这个socket已经被设置成-1
                     * 所以后面的close_socket()函数都会报错
                     */
                    continue;
                }
                /*
                 * 此处不能continue,因为每个socket都可能有多个事件同时发送到服务器端
                 * 这也是下面语句用if而不是if-else的原因,
                 */

            }
            //客户端有事件到达
            if (events[i].events & EPOLLERR)
            {
                printf("EPOLLERR\n");
                //返回出错事件,关闭socket,清理epoll池
                close_socket(events[i].data.fd);
                events[i].data.fd = -1;
                continue;
            }
            //客户端有事件到达
            if (events[i].events & EPOLLHUP)
            {
                printf("EPOLLHUP\n");
                //返回挂起事件,关闭socket,清理epoll池
                close_socket(events[i].data.fd);
                events[i].data.fd = -1;
                continue;
            }
        }
    }
    //close epoll
    close(epfd);
    //close server socket
    close_socket(listen_st);
    return 0;
}
//网络编程客户端
#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<2)
    {
        printf("please print two param !\n");
    }
    //端口号
    int port=atoi(args[2]);
    //服务端IP地址
    char ipaddr[30]={0};
    strcpy(ipaddr,args[1]);
    //connect server
    int st=connect_server(ipaddr,port);
    //send message
    //发送消息--
    socket_send(st);
    //close socket
    close(st);
    return 0;
}
.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 [email protected] -c $<
clean:
    rm -f $(OBJS1)
    rm -f $(EXEC1)
    rm -f $(OBJS2)
    rm -f $(EXEC2)
时间: 2024-10-16 17:14:51

Linux 网络编程八(epoll应用--大并发处理)的相关文章

嵌入式 Linux网络编程(五)——epoll机制

嵌入式 Linux网络编程(五)--epoll机制 一.epoll简介 epoll是在2.6内核中提出的,是select和poll的增强版本.epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中. 1.epoll函数 #include <sys/epoll.h> int epoll_create(int size); 创建一个epoll的句柄,size表示监听的文件描述的数量 int epoll_ctl(int epfd,

Linux网络编程——多路复用之epoll

目录 Linux网络编程--多路复用之epoll 基础API 实例一.epoll实现在线聊天 实例二.epoll实现在客户端断开后服务端能一直运行,客户端可以多次重连 Linux网络编程--多路复用之epoll ? epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,

Linux网络编程 五种I/O 模式及select、epoll方法的理解

Linux网络编程 五种I/O 模式及select.epoll方法的理解 web优化必须了解的原理之I/o的五种模型和web的三种工作模式 五种I/O 模式--阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/OLinux网络编程 五种I/O 模式及select.epoll方法的理解

Linux网络编程入门 (转载)

http://www.cnblogs.com/RascallySnake/archive/2012/01/04/2312564.html (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件

Linux网络编程入门

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍 客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端 在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一 个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端 和客户端相对应的程序即为服务端程序.被动的等待外面的程序来和自己通

linux网络编程学习笔记之六 -----I/O多路复用服务端

多进程和多线程的目的是在于最大限度地利用CPU资源,当某个进程不需要占用太多CPU资源,而是需要I/O资源时,可以采用I/O多路复用,基本思路是让内核把进程挂起,直到有I/O事件发生时,再把控制返回给程序.这种事件驱动模型的高效之处在于,省去了进程和线程上下文切换的开销.整个程序运行在单一的进程上下文中,所有的逻辑流共享整个进程的地址空间.缺点是,编码复杂,而且随着每个逻辑流并发粒度的减小,编码复杂度会继续上升. I/O多路复用典型应用场合(摘自UNP6.1) select的模型就是这样一个实现

[转] - Linux网络编程 -- 网络知识介绍

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端        和客户端相

Linux网络编程视频 百度网盘

Linux网络编程(总共41集)讲解Linux网络编程知识,分以下四个篇章.Linux网络编程之TCP/IP基础篇Linux网络编程之socket编程篇Linux网络编程之进程间通信篇Linux网络编程之线程篇Linux网络编程之TCP/IP基础篇01TCPIP基础(一)ISO/OSI参考模型TCP/IP四层模型基本概念(对等通信.封装.分用.端口)02TCPIP基础(二)最大传输单元(MTU)/路径MTU以太网帧格式ICMPARPRARP03TCPIP基础(三)IP数据报格式网际校验和路由04

很全的linux网络编程技巧

注:作者王晓,本人认为总结得很好,故记之,绝无侵权之意. 1. LINUX网络编程基础知识 1 1.1. TCP/IP协议概述 1 1.2. OSI参考模型及TCP/IP参考模型 1 1.3. TCP协议 3 1.4. UDP协议 5 1.5. 协议的选择 6 2. 网络相关概念 6 2.1. socket概念 7 2.2. socket类型 8 2.3. socket信息数据结构 8 2.4. 数据存储优先顺序的转换 8 2.5. 地址格式转化 9 2.6. 名字地址转化 10 3. sock