快捷搜索:
来自 67677新澳门手机版 2019-09-01 06:23 的文章
当前位置: 67677新澳门手机版 > 67677新澳门手机版 > 正文

c语言多进程tcp服务器示例

网络上所有资料都说epoll是高并发、单线程、IO重叠服用的首选架构,比select和poll性能都要好,特别是在有大量不活跃连接的情况下。具体原理就不阐述了,下面说说使用。

server.h

具有有三个函数:

复制代码 代码如下:

#include <sys/epoll.h>

#ifndef SERVER_H
#define SERVER_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/epoll.h>
#include <signal.h>
#include <fcntl.h>
#include "bussiness.h"
#define LISTENTQ 5
#define DEF_PORT 6000
#define MAX_EPOLL_SIZE 10
#define DEF_PROCESS_NUM 5
int create_tcpsvr(char *ip, int port);
void fill_sockaddr(struct sockaddr_in *addr,char *ip, int port);
void comm_to_client(int sockfd);
void epoll_business(int epollfd, int listenfd, struct epoll_event ev);
int init_epoll(int listenfd);
void create_process(int sockfd, int i);
#endif

 

server.c

1、int epoll_create ( int size );

复制代码 代码如下:

size是epoll要监视的fd的规模。

#include "server.h"
/*
 * Create a TCP Server
 * Return socketfd
 */
int create_tcpsvr(char *ip,int port)
{
    int sockfd;
    struct sockaddr_in addr;

 

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == EOF){
        perror("create_tcpsvr(),socket()");
        exit(EXIT_FAILURE);
    }

2、int epoll_ctl ( int epfd, int op, int fd, struct epoll_event *event );

    fill_sockaddr(&addr,ip,port);

(1)epfd:epoll_create的返回值。

    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == EOF){
        perror("create_tcpsvr(),bind()");
        exit(EXIT_FAILURE);
    }

(2)op  指定操作类型:

    if (listen(sockfd,LISTENTQ) == EOF) {
        perror("create_tcpsvr(),bind()");
        exit(EXIT_FAILURE);
    }
    return sockfd;
}

     EPOLL_CTL_ADD:往事件表中注册fd上的事件

/**
 * Set TCP server's address
 */
void fill_sockaddr(struct sockaddr_in *addr, char *ip, int port)
{
    assert(addr);

     EPOLL_CTL_MOD:修改fd上的注册事件

    addr -> sin_family = AF_INET;
    addr -> sin_port = htons(port?port:DEF_PORT);
    if (ip == NULL) {
        addr -> sin_addr.s_addr = htonl(INADDR_ANY);
    } else if((addr -> sin_addr.s_addr = inet_addr(ip)) == EOF) {
       perror("fill_sockaddr(),inet_addr()");
       exit(EXIT_FAILURE);
    }
}
/*
 * Communicate to client
 */
void comm_to_clt(int listenfd)
{   
    printf("TCP SERVER IS WAITING67677新澳门手机版 ,!n");

     EPOLL_CTL_DEL:删除fd上的注册事件

    struct epoll_event events[MAX_EPOLL_SIZE];
    int fd_num;

(3)fd:要操作的文件描述符(socket)

    int epollfd = init_epoll(listenfd);

 (4)event:指定要监听fd的什么事情。它是epoll_event结构指针类型:

    while (1) {

struct epoll_event

        fd_num = epoll_wait(epollfd,events,MAX_EPOLL_SIZE,-1);
        if (fd_num == EOF) {
            perror("comm_to_clt(),epoll_wait()");
            continue;
        }
        while (--fd_num >= 0) {
            epoll_business(epollfd, listenfd, events[fd_num]);
        }
    }   
}
/*
 * Initlization epoll
 */
int init_epoll(int listenfd)
{
    struct epoll_event ev;
    int epollfd;

{

    if((epollfd = epoll_create(MAX_EPOLL_SIZE)) == EOF) {
        perror("init_epoll(),epoll_create()");
        exit(EXIT_FAILURE);
    }

     __unit32_t events;    // epoll事件

    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD,listenfd, &ev)) {
        perror("init_epoll(),epoll_ctl()");
        exit(EXIT_FAILURE);
    }
    return epollfd;
}

     epoll_data_t data;     // 用户数据

/*
 * Respond for io change
 */
void epoll_business(int epollfd, int listenfd, struct epoll_event ev)
{
    struct epoll_event ev_n;
    if (ev.data.fd == listenfd) {
        int clt_fd = accept(listenfd, 0, 0);
        if (clt_fd == EOF) {
            perror("epoll_business(), accept()");
            return;
        }

};

        fcntl(clt_fd, F_SETFL, O_NONBLOCK);
        ev_n.events = EPOLLIN|EPOLLET;
        ev_n.data.fd = clt_fd;
        if (epoll_ctl(epollfd,EPOLL_CTL_ADD, clt_fd, &ev_n) == EOF) {
            perror("epoll_business(), epoll_ctl()");
            return;
        }
    }else {
        main_service(ev.data.fd);
        if (epoll_ctl(epollfd,EPOLL_CTL_DEL, ev.data.fd, &ev) == EOF) {
            perror("epoll_business(), epoll_ctl()");
        }
        close(ev.data.fd);
    }
}
/*
 * Create some process
 */

events:描述事件类型。events可以是以下几个宏的集合:

void create_process(int sockfd, int i)
{
    /*
     * Avoid zombie process
     */
    signal(SIGCHLD,SIG_IGN);

 EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;

    while (i--) {
        if (fork() == 0) {
            comm_to_clt(sockfd);
        }
    }
    while(1){
        sleep(100);
    }
}
int main(int argc, char *argv[])
{
    int sockfd = create_tcpsvr(0, 0);
    create_process(sockfd,DEF_PROCESS_NUM);
    exit(EXIT_SUCCESS);
}

 EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;

bussiness.h

EPOLLHUP:表示对应的文件描述符被挂断;

复制代码 代码如下:

 EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

#ifndef BUSSINESS_H
#define BUSSINESS_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#define BUFF_SIZE 4096
void main_service(int sockfd);
int write_sock(int sockfd, char *buff, size_t length);
int read_sock(int sockfd, char *buff, size_t llength);
#endif

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

bussiness.c

data成员:其中data.fd常用来装要操作的fd。

复制代码 代码如下:

 

#include "bussiness.h"

typedef union epoll_data

void main_service(int sockfd)
{
    char buff[BUFF_SIZE];
    size_t buff_len;
    if ((buff_len = read_sock(sockfd, buff, BUFF_SIZE)) == EOF) {
        perror("main_service(),read_sock()");
        return;
    } else {
  //业务逻辑从这里开始
        printf("%s", buff);
    }

{

}
/*
 * Recive messages
 * Return the length
 */
int read_sock(int sockfd, char *buff, size_t length)
{
    assert(buff);
    return read(sockfd, buff, length);
}
/*
 * Send messages
 * Return the length
 */
int write_sock(int sockfd, char *buff,size_t length)
{
    assert(buff);
    return write(sockfd, buff, length);
}

     void *ptr;

您可能感兴趣的文章:

  • C语言编写Linux守护进程实例
  • Linux中使用C语言的fork()函数创建子进程的实例教程
  • 举例讲解C语言的fork()函数创建子进程的用法
  • C语言实现在windows服务中新建进程的方法
  • 用c语言实现HUP信号重启进程的方法
  • Linux下C语言修改进程名称的方法
  • C语言中设置进程优先顺序的方法
  • C语言中操作进程信号的相关函数使用详解
  • C语言怎么获得进程的PE文件信息
  • Linux下C语言的fork()子进程函数用法及相关问题解析
  • C语言中获取进程识别码的相关函数
  • C语言实现查看进程是否存在的方法示例

      int fd;

      __uint32_t u32;

      __uint64_t u64;

  } epoll_data_t;

 

 

3、int epoll_wait ( int epfd, struct epoll_event* events, int maxevents, int timeout );

epoll_wait的工作流程是:等待,如果有epoll事件发生立刻返回,否则等待timeout毫秒。返回时拷贝要处理的事件到events指向的数组,返回就绪的文件描述符的个数,失败时返回-1并设置errno。 

timeout:指定epoll的超时时间,单位是毫秒。当timeout为-1是,epoll_wait调用将永远阻塞,直到某个事件发生。当timeout为0时,epoll_wait调用将立即返回。 

maxevents:指定最多监听多少个事件。如果events指向的是20个单元的结构体数组,那么就设置maxevents为20。 

events: events指向的数组中将拷贝所有就绪的事件,从内核事件表中。events的成员是struct epoll_event类型,一般包含events(其值是如:EPOLLIN、EPOLLOUT等)。还有一个是data.fd,包含拷贝的事件对应的socket,如果是服务器监听socket,说明是有用户连接到这个socket,如果是其他已经连接好的socket,说明是有数据要发送或者接收。

如果事件数组中的数据得到处理,那么内核事件表中的事件就会被删除,下次wait就没有那些socket的事件了。 

 

实例代码:

epoll.c

#include <stdio.h>

#include <sys/epoll.h>

#include <sys/socket.h>

#include <stdlib.h>

#include <netinet/in.h>                 //包含sockaddr_in定义

#include <errno.h>

#include <string.h>                     //包含memset strncpy

 

 

int main(int argc,char* argv[])   //主函数

{

 

      int epfd1;int result;

      int server_len,client_len;

      int server_sockfd,client_sockfd;

      struct sockaddr_in server_address;       //定义在 <netinet/in.h>

      struct sockaddr_in client_address;

      struct epoll_event ev1;

      struct epoll_event ev[20];

      int epollreturn;

      int i,j,res;

      int sockfd;

      char ch = '0';

      char buff[1024];

     

      server_address.sin_family = AF_INET;

      server_address.sin_addr.s_addr = inet_addr("192.168.131.129");

      server_sockfd = socket(AF_INET,SOCK_STREAM,0);

      server_address.sin_port = htons(9734);

      server_len = sizeof(server_address);

      client_len = sizeof(client_address);

     

      result = bind(server_sockfd,(struct sockaddr*)&server_address,server_len);

      if(result!=0)

      {

           printf("bind failedn");

           exit(1);                             //在stdlib.h

      }   

 

     

     

      epfd1 = epoll_create(10000);

      ev1.data.fd = server_sockfd;

      ev1.events = EPOLLIN;

      /*

      printf("xn",EPOLLIN);

      printf("xn",EPOLLOUT);

      printf("xn",EPOLLPRI);

      printf("xn",EPOLLERR);

      printf("xn",EPOLLHUP);

      printf("xn",EPOLLET);

      printf("xn",EPOLLONESHOT);

      */

      epoll_ctl(epfd1,EPOLL_CTL_ADD,server_sockfd,&ev1);

     

     

     

      result = listen(server_sockfd,5);

      if(result!=0)

      {

           printf("listen failedn");

           exit(1);

      }

     

      memset(buff,0,1024);

      strncpy(buff,"this is server",14);

     

      for(;;)

      {   

           epollreturn  = epoll_wait(epfd1,ev,20,4000);

           printf("epollreturn is %dn",epollreturn);

           if(epollreturn>0)

           {

                 for(i=0;i<epollreturn;i )

                 {

                     

                     if(ev[i].data.fd==server_sockfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。

                      {

                           

                            client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, &client_len);//没有计算client_len的值,会导致accept返回-1

                            printf("accept one client,socket:%dn",client_sockfd);                        

                            ev1.data.fd=client_sockfd;

                            ev1.events=EPOLLIN;

                            epoll_ctl(epfd1,EPOLL_CTL_ADD,client_sockfd,&ev1);

                           

                            //ev1.data.fd=client_sockfd;

                            //ev1.events=EPOLLOUT;

                            //epoll_ctl(epfd1,EPOLL_CTL_ADD,client_sockfd,&ev1); //注册

                      }

                      else if(ev[i].events&EPOLLIN)//如果是已经连接的用户,收到数据,那么进行读入。

                      {

                            sockfd = ev[i].data.fd;

本文由67677新澳门手机版发布于67677新澳门手机版,转载请注明出处:c语言多进程tcp服务器示例

关键词: