网络编程,socket基础

TCP三次握手

        发生在协议栈中,只要服务器相应的端口处于listen状态就可连接,不属于应用态

网络IO模型

1.一个进程多个线程

 优点:逻辑简单,缺点:不适合大量的客户端请求,阻塞较多(accept + recv都是阻塞的),不知道connfd到底属于哪个线程,解决方法:使用IO多路复用,将这个工作交给组件(select,epoll等)去实现

难以突破C10k

2.一个进程一个select

能做到C10k,难以做到C1000K

3.rector模型

IO多路复用

select原理:一个client一个fd,通过select来选择有事件发生的fd,比特位来判断是否有事件发生(fd_set ,一个比特集合的数据结构),select五个参数

内部,先初始化一个表示事件的发生与否的fd_set,通过FD_ZERO将fd_set置0,然后注册

代码

fd_set rdfs;
FD_ZERO(&rdfs);
FD_SET(listenfd,rdfs);

select代码

int main(int argc, char *argv[])
{
    if (argc <= 2)
    {
        printf("usage:  ip_address port_number\n");
        return 1;
    }
    const char *ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);

    int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
    assert(ret != -1);
    ret = listen(sock, 5);
    assert(ret != -1);
    //两个fdset,一个注册给内核,一个用户使用
    fd_set rfds, rset,wfds,wset;
    //将内核中的相应位全部置0
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
    char buff[MAX_LEN];
    //注册listenfd到内核
    FD_SET(sock, &rfds);
    //循环侦听
    int connfd;
    int max_fd = sock;
    int n;
    while (1) {

		rset = rfds;
		wset = wfds;

		int nready = select(max_fd+1, &rset, &wset, NULL, NULL);


		if (FD_ISSET(sock, &rset)) { //

			struct sockaddr_in client;
		    socklen_t len = sizeof(client);
		    if ((connfd = accept(sock, (struct sockaddr *)&client, &len)) == -1) {
		        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
		        return 0;
		    }

			FD_SET(connfd, &rfds);

			if (connfd > max_fd) max_fd = connfd;

			if (--nready == 0) continue;

		}

		int i = 0;
		for (i = sock+1;i <= max_fd;i ++) {

			if (FD_ISSET(i, &rset)) { // 
				n = recv(i, buff, MAX_LEN, 0);
		        if (n > 0) {
		            buff[n] = '\0';
		            printf("recv msg from client: %s\n", buff);
					FD_SET(i, &wfds);

					//reactor
					//send(i, buff, n, 0);
		        } else if (n == 0) { //

					FD_CLR(i, &rfds);
					//printf("disconnect\n");
		            close(i);
					
		        }
				if (--nready == 0) break;
			} 
            else if (FD_ISSET(i, &wset)) {
                printf("inter the send %d \n",i);
				send(i, buff, n, 0);
				FD_SET(i, &rfds);

                //这里要将写的fd清除出去,否则服务器会一直发
                FD_CLR(i,&wfds);
				if (--nready == 0) break;
			}
		}
    }

    close(sock);
    return 0;
}

poll代码

 struct pollfd fds[POLL_SIZE] = {0};
    fds[0].fd = sock;
    fds[0].events = POLLIN;
    while (1)
    {
        int nready = poll(fds, max_fd, -1);
        if (fds[0].revents & POLLIN)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            if ((connfd = accept(sock, (struct sockaddr *)&client, &len)) == -1)
            {
                printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                return 0;
            }
            fds[connfd].fd = connfd;
            fds[connfd].events = POLLIN;
            if (connfd > max_fd)
            {
                max_fd = connfd;
            }
            if (--nready == 0)
            {
                continue;
            }
        }

        int i = 0;
        for (i = sock + 1; i <= max_fd; ++i)
        {
            if (fds[i].revents & POLLIN)
            {
                n = recv(i, buff, MAX_LEN, 0);
                if (n > 0)
                    buff[n] = '\0';
                fds[i].events = POLLOUT;
            }
            else if (fds[i].revents & POLLOUT)
            {
                send(i, buff, n, 0);
                fds[i].events = POLLIN;
            }
        }
    }

epoll代码:                

int epfd = epoll_create(1);
    //存储活跃的event
    struct epoll_event events[POLL_SIZE] = {0};

    //处理连接的事件
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sock;
    //将处理连接的端口加入进去
    epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev);
    while (1)
    {
        int nready = epoll_wait(epfd, events, POLL_SIZE, 5);
        if (nready == -1)
        {
            continue;
        }
        int i = 0;
        for (i = 0; i < nready; ++i)
        {   
            if (events[i].data.fd == sock)
            {
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                if ((connfd = accept(sock, (struct sockaddr *)&client, &len)) == -1)
                {
                    printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                    return 0;
                }
                ev.events = EPOLLIN;
                ev.data.fd = connfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
            }
            else if(events[i].events & EPOLLIN)
            {   
                int n =  recv(events[i].data.fd,buff,MAX_LEN,0);
                buff[n] = '\0';
                ev.events = EPOLLOUT;
                ev.data.fd = events[i].data.fd;
                epoll_ctl(epfd,EPOLL_CTL_MOD,ev.data.fd,&ev);
            }
            else if(events[i].events & EPOLLOUT){
                send(events[i].data.fd,buff,n,0);
                events[i].events = EPOLLIN;
                epoll_ctl(epfd,EPOLL_CTL_MOD,events[i].data.fd,&events[i]);
            }
        }
    }

epoll原理:

基于io/fd/socket,

epoll_create():创建epoll事件。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/epoll.h>
#include <string.h>
#include <pthread.h>
#include <sys/poll.h>
#define MAX_LEN 1024
#define MAX_EPOLL_EVENT 1024
#define POLL_SIZE 1024


typedef int NCALLBACK(int fd, int event, void *arg);
int ncreator_del(int fd, NCALLBACK cb, int event, void *arg);
//设置回调函数
// epoll池中管理的对象
typedef struct nitem
{
    int fd;
    //事件,通过int来表示
    int events;
    void *arg;
    //一个回调函数的情况,epoll处理时判断可读还是可写

    //两个回调函数,两者可以并存
    NCALLBACK *readcb;
    NCALLBACK *writecb;
    NCALLBACK *accpetcb;
    // ASCII大于0,只需要存储大于0的情况
    unsigned char sbuff[MAX_LEN];
    int slenght;
    unsigned char rbuff[MAX_LEN];
    int rlength;
} nitem;

typedef struct itemblock
{
    struct itemblock *next;
    nitem *items;
} itemblock;

//链表+数组方式来存储事件
struct reactor
{
    int epfd;
    itemblock *first; 
};


int nreactor_set_event(int fd, NCALLBACK cb, int event, void *arg);
int init_reactor(struct reactor *r)
{
    int epfd = epoll_create(1);
    // fd与item的关系为直接的数组下标
    r->first = (struct itemblock *)malloc(sizeof(itemblock));
    r->epfd = epfd;
    if (r->first == NULL)
    {
        close(epfd);
        return -2;
    }
    memset(r->first, 0, sizeof(itemblock));
    //分配事件
    r->first->items = (nitem *)malloc(MAX_EPOLL_EVENT * sizeof(nitem));
    if (r->first->items == NULL)
    {   
        close(epfd);
        return -2;
    }
    memset(r->first->items, 0,(MAX_EPOLL_EVENT * sizeof(nitem)));
    r->first->next = NULL;
    return 0;
}
//单例模式的全局reactor
struct reactor *g_reactor = NULL;
struct reactor *getinstance(void)
{
    if (g_reactor == NULL)
    {
        g_reactor = (struct reactor *)malloc(sizeof(struct reactor));
        if (NULL == g_reactor)
        {
            return NULL;
        }
        memset(g_reactor, 0, sizeof(struct reactor));
        if (init_reactor(g_reactor) < 0)
        {
           return NULL;
        }
    }
    return g_reactor;
}
#define NO_SET_CB 0
#define READ_CB 1
#define WRITE_CB 2
#define ACCEPT_CB 3

int read_callback(int fd,int event,void *arg);
int write_callback(int fd,int event,void *arg){
    struct reactor * r = getinstance();
    unsigned char *sbuff = r->first->items[fd].sbuff;
    int length = r->first->items[fd].slenght;
    int ret = send(fd,sbuff,length,0);
    if(ret < length){
        nreactor_set_event(fd,write_callback,WRITE_CB,NULL);
    }
    else{
        nreactor_set_event(fd,read_callback,READ_CB,NULL);
    }
    return 0;
}

int read_callback(int fd,int event,void *arg){
    struct reactor * r = getinstance();
    unsigned char *buffer = r->first->items[fd].rbuff;
#if 0
    while(index < MAX_LEN){
        ret = recv(fd,buffer+index,MAX_LEN - index,0);
        if(ret == -1){
            //break;
        }
        else if(ret > 0){
            index += ret;
            //加入到
            nreactor_set_event(fd,read_callback,READ_CB,NULL);
        }
        else{
           // break;
        }
    //}
#endif
    int ret = recv(fd,buffer,MAX_LEN,0);
    if(ret == 0){
        ncreator_del(fd,NULL,0,NULL);
        close(fd);
    }
    else if(ret > 0){
        unsigned char * sbuffer = r->first->items[fd].sbuff;
        memcpy(sbuffer,buffer,ret);
        r->first->items[fd].slenght = ret;
        printf("read cb : %s\n",sbuffer);
        nreactor_set_event(fd,write_callback,WRITE_CB,NULL);
    }
}

int accpet_callback(int fd, int event, void *arg)
{
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    int connfd;
    if ((connfd = accept(fd, (struct sockaddr *)&client, &len)) == -1)
    {
        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    printf("accept sucess\n");
    nreactor_set_event(connfd,read_callback,READ_CB,NULL);
}





int init_server(int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    assert(sock != -1);
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    address.sin_port = htons(port);
    assert(sock >= 0);
    int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
    assert(ret != -1);
    ret = listen(sock, 10);
    assert(ret != -1);
    return sock;
}

int nreactor_set_event(int fd, NCALLBACK cb, int event, void *arg)
{
    struct reactor *r = getinstance();
    struct epoll_event ev = {0};
    

    if (event == READ_CB)
    {
        r->first->items[fd].fd = fd;
        r->first->items[fd].readcb = cb;
        r->first->items[fd].arg = arg;
        ev.events = EPOLLIN;
    }
    else if (event == WRITE_CB)
    {
        r->first->items[fd].fd = fd;
        r->first->items[fd].writecb = cb;
        r->first->items[fd].arg = arg;
        ev.events = EPOLLOUT;
    }
    else if (event == ACCEPT_CB)
    {
        r->first->items[fd].fd = fd;
        r->first->items[fd].accpetcb = cb;
        r->first->items[fd].arg = arg;
        ev.events = EPOLLIN;
    }
    ev.data.ptr = &r->first->items[fd]; 
    if(r->first->items[fd].events == NO_SET_CB){
        if (epoll_ctl(r->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
			printf("epoll_ctl EPOLL_CTL_ADD failed, %d\n", errno);
			return -1;
		}
		r->first->items[fd].events = event;
    }
    else if(r->first->items[fd].events != event){
        if (epoll_ctl(r->epfd, EPOLL_CTL_MOD, fd, &ev) < 0) {
			printf("epoll_ctl EPOLL_CTL_MOD failed\n");
			return -1;
		}
		r->first->items[fd].events = event;
    }
    return 0;
}

int reactor_loop(int listenfd){ 
    struct epoll_event events[MAX_EPOLL_EVENT] = {0};
    struct reactor* r = getinstance();
    while (1)
    {
        int nready = epoll_wait(r->epfd, events, POLL_SIZE, 5);
        if (nready == -1)
        {
            continue;
        }
        int i = 0;
        for (i = 0; i < nready; ++i)
        {   
            struct nitem *item = (struct nitem *)events[i].data.ptr;
            int connfd = item->fd;
            if (connfd == listenfd)
            {
                item->accpetcb(listenfd,0,NULL);
            }
            else{
                if(events[i].events & EPOLLIN){
                    item->readcb(connfd,0,NULL);
                }
                if(events[i].events &EPOLLOUT){
                    item->writecb(connfd,0,NULL);
                }
            }
        }
    }
    return 0;
}

int ncreator_del(int fd, NCALLBACK cb, int event, void *arg){
    struct reactor * r = getinstance();
    struct epoll_event ev = {0};
    ev.data.ptr = arg;
    epoll_ctl(r->epfd,EPOLL_CTL_DEL,fd,&ev);
    r->first->items[fd].events = 0;
    return 0;
}

int main(int argc, char *argv[])
{
    //两个fdset,一个注册给内核,一个用户使用
    char buff[MAX_LEN];
    //封装到init_server函数中
    int listenfd = init_server(8080);
    //将监听端口连接加入到epoll中
    nreactor_set_event(listenfd, accpet_callback, ACCEPT_CB, NULL);
    reactor_loop(listenfd);
    close(listenfd);
    return 0;
}

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值