用c++写一个socket和epoll网络编程的服务器

服务器

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>

#define PORT 1500//端口号 
#define BACKLOG 5/*最大监听数*/ 

int main(){
	int sockfd,new_fd;/*socket句柄和建立连接后的句柄*/
	struct sockaddr_in my_addr;/*本方地址信息结构体,下面有具体的属性赋值*/
	struct sockaddr_in their_addr;/*对方地址信息*/
	int sin_size;

	sockfd=socket(AF_INET,SOCK_STREAM,0);//建立socket 
	if(sockfd==-1){
		printf("socket failed:%d",errno);
		return -1;
	}
	my_addr.sin_family=AF_INET;/*该属性表示接收本机或其他机器传输*/
	my_addr.sin_port=htons(PORT);/*端口号*/
	my_addr.sin_addr.s_addr=htonl(INADDR_ANY);/*IP,括号内容表示本机IP*/
	bzero(&(my_addr.sin_zero),8);/*将其他属性置0*/
	if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0){//绑定地址结构体和socket
		printf("bind error");
		close(sockfd);
		return -1;
	}
    listen(sockfd,BACKLOG);//开启监听 ,第二个参数是最大监听数 
    while(1){
    	sin_size=sizeof(struct sockaddr_in);
    	new_fd=accept(sockfd,(struct sockaddr*)&their_addr,&sin_size);//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小 
    	if(new_fd==-1){
    		printf("receive failed");
		} else{
			printf("receive success");
			send(new_fd,"Hello World!",12,0);//发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可) 
		}
	}
	return 0;
} 

客户端

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>

 
#define DEST_PORT 1500//目标地址端口号 
#define DEST_IP "127.0.0.1"/*目标地址IP,这里设为本机*/ 
#define MAX_DATA 100//接收到的数据最大程度 

int main(){
	int sockfd,new_fd;/*cocket句柄和接受到连接后的句柄 */
	struct sockaddr_in dest_addr;/*目标地址信息*/
	char buf[MAX_DATA];//储存接收数据 

	sockfd=socket(AF_INET,SOCK_STREAM,0);/*建立socket*/
	if(sockfd==-1){
		printf("socket failed:%d",errno);
	}

	//参数意义见上面服务器端 
	dest_addr.sin_family=AF_INET;
 	dest_addr.sin_port=htons(DEST_PORT);
	dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);
	bzero(&(dest_addr.sin_zero),8);
	
	if(connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr))==-1){//连接方法,传入句柄,目标地址和大小 
		printf("connect failed:%d",errno);//失败时可以打印errno 
		close(sockfd);
		return -1;
	} else{
		printf("connect success");
		recv(sockfd,buf,MAX_DATA,0);//将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。 
		printf("Received:%s",buf);
	}
	close(sockfd);//关闭socket 
	return 0;
} 

epoll函数原型:

1int epoll_create(int size); 
创建一个epoll接口,返回一个epoll描述符,后面要用到。

2int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
用来从epoll接口中添加fd或删除、修改fd。
(1)epfd是接口描述符,
(2)op是添加、修改、删除三者,fd是要操作的对象,event是监视的事件。结构如下:
typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;
           
           struct epoll_event {
               uint32_t     events;      /* Epoll events */
               epoll_data_t data;        /* User data variable */
           };
           
3int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);1)events是输出型的参数,返回的是准备好的描述符,
(2)maxevents是事件数量的上限
(3)timeout是超时,以毫秒为单位,-1代表用不超时,0代表不等待立即返回。


服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/un.h>
 
#define MAX_FD_NUM 3
#define PATH "un.sock"
 
void setnonblock(int fd) {
    int flag = fcntl(fd, F_GETFL, 0);
    if (flag == -1) {
        printf("get fcntl flag %s\n", strerror(errno));
        return;
    }
    int ret = fcntl(fd, F_SETFL, flag | O_NONBLOCK);
    if (ret == -1) {
        printf("set fcntl non-blocking %s\n", strerror(errno));
        return;
    }
}
 
int socket_create() {
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd == -1) {
        printf("socket create %s\n", strerror(errno));
        return -1;
    }
    setnonblock(fd);
    struct sockaddr_un addr;

    unlink(PATH);
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, PATH);

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    	printf("socket bind: %s\n", strerror(errno));
        return -1;
    }
    if (listen(fd, 20) == -1) {
        printf("socket listen %s\n", strerror(errno));
        return -1;
    }
    return fd;
}
 
void socket_accept(int fd) {
    struct epoll_event event, events[MAX_FD_NUM];
    int client_fd;
    int epfd = epoll_create(MAX_FD_NUM);
    if (epfd == -1) {
        printf("epoll create %s\n", strerror(errno));
        return;
    }
    
    memset(&event, 0, sizeof(event));
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLERR | EPOLLHUP;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event) == -1) {
        printf("epoll ctl %s\n", strerror(errno));
        return;
    }
    while (1) {
        int num = epoll_wait(epfd, events, MAX_FD_NUM, -1);
        if (num == -1) {
            printf("epoll wait %s\n", strerror(errno));
            break;
        } else {
            int i = 0;
            for (; i<num; ++i) {
                if (events[i].data.fd == fd) {
                    struct sockaddr_un client_addr;
                    memset(&client_addr, 0, sizeof(client_addr));
                    int len = sizeof(client_addr);
                    client_fd = accept(fd, (struct sockaddr *)&client_addr, &len);
                    if (client_fd == -1) {
                        printf("socket accept %s\n", strerror(errno));
                        return;
                    } else {
                        printf("socket accept success. fd=%d, client_fd=%d\n", fd, client_fd);
		    		}
                    setnonblock(client_fd);
                    event.data.fd = client_fd;
                    event.events = EPOLLIN | EPOLLERR | EPOLLHUP;
                    if (epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
                        printf("epoll ctl %s\n", strerror(errno));
                        return;
                    }
                    continue;
                } else if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) {
                    printf("epoll err\n");
                    close(events[i].data.fd);
                    continue;
                } else {
                    char buf[64];
                    memset(buf, 0, sizeof(buf));
                    recv(events[i].data.fd, buf, sizeof(buf), 0);
                    printf("recv msg: %s", buf);
                    close(events[i].data.fd);
                    continue;
                }
            }
        }
    }
}
 
int main(int argc, char *argv[]) {
    int fd = socket_create();
    if (fd == -1) {
        printf("socket create fd failed\n");
        return;
    }
    socket_accept(fd);
    return 0;
}


#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>

const int MAX_EVENTS = 10; // epoll_wait每次最多处理的事件数

int main() {
    // 创建监听socket
    int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == -1) {
        std::cerr << "Error creating socket." << std::endl;
        return 1;
    }

    // 设置服务器地址信息
    sockaddr_in serverAddress;
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY; // 使用任意可用地址
    serverAddress.sin_port = htons(8888); // 服务器监听的端口

    // 绑定socket和地址
    if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
        std::cerr << "Error binding socket." << std::endl;
        close(serverSocket);
        return 1;
    }

    // 开始监听连接
    if (listen(serverSocket, 5) == -1) {
        std::cerr << "Error listening for connections." << std::endl;
        close(serverSocket);
        return 1;
    }

    std::cout << "Server started. Listening for connections..." << std::endl;

    // 创建epoll实例
    int epollFd = epoll_create1(0);
    if (epollFd == -1) {
        std::cerr << "Error creating epoll instance." << std::endl;
        close(serverSocket);
        return 1;
    }

    // 设置epoll事件结构体
    epoll_event event;
    event.events = EPOLLIN; // 监听可读事件
    event.data.fd = serverSocket; // 监听的文件描述符

    // 将监听socket添加到epoll实例中
    if (epoll_ctl(epollFd, EPOLL_CTL_ADD, serverSocket, &event) == -1) {
        std::cerr << "Error adding socket to epoll." << std::endl;
        close(epollFd);
        close(serverSocket);
        return 1;
    }

    // 创建用于存储活动事件的数组
    epoll_event events[MAX_EVENTS];

    // 开始接受客户端连接和处理数据
    while (true) {
        // 等待事件发生
        int numEvents = epoll_wait(epollFd, events, MAX_EVENTS, -1);
        if (numEvents == -1) {
            std::cerr << "Error in epoll_wait." << std::endl;
            close(epollFd);
            close(serverSocket);
            return 1;
        }

        // 处理所有就绪的事件
        for (int i = 0; i < numEvents; ++i) {
            // 如果是监听socket有新连接
            if (events[i].data.fd == serverSocket) {
                sockaddr_in clientAddress;
                socklen_t clientAddrSize = sizeof(clientAddress);
                int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddrSize);
                if (clientSocket == -1) {
                    std::cerr << "Error accepting client connection." << std::endl;
                    continue;
                }

                // 将新的客户端socket添加到epoll实例中
                event.events = EPOLLIN; // 监听可读事件
                event.data.fd = clientSocket;
                if (epoll_ctl(epollFd, EPOLL_CTL_ADD, clientSocket, &event) == -1) {
                    std::cerr << "Error adding client socket to epoll." << std::endl;
                    close(clientSocket);
                    continue;
                }

                std::cout << "New client connected. Client socket: " << clientSocket << std::endl;
            } else {
                // 处理已连接客户端的数据
                char buffer[1024] = {0};
                int bytesRead = recv(events[i].data.fd, buffer, sizeof(buffer), 0);
                if (bytesRead <= 0) {
                    // 客户端断开连接或出错
                    std::cout << "Client disconnected or error occurred. Client socket: " << events[i].data.fd << std::endl;
                    epoll_ctl(epollFd, EPOLL_CTL_DEL, events[i].data.fd, nullptr);
                    close(events[i].data.fd);
                } else {
                    // 打印接收到的消息
                    std::cout << "Received from client " << events[i].data.fd << ": " << buffer << std::endl;

                    // 发送回复消息给客户端
                    send(events[i].data.fd, buffer, bytesRead, 0);
                }
            }
        }
    }

    // 关闭连接
    close(serverSocket);
    close(epollFd);

    return 0;
}

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>

#define BUFFER_SIZE 10
#define MAX_ENVENT_NUM 1024

// 把sockfd修改为非阻塞的 (对sockfd的修改适合放在Socket类中)
int setnonblocking(int sockfd)
{
    int old_option = fcntl(sockfd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(sockfd, F_SETFL, new_option);
    return old_option;
}

// 把fd上的可读事件注册到epollfd对应的epoller上(epoll操作相关的都可以放在EPollPoller类中)
void addfd(int epollfd, int fd)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;         
    // 注册fd上的可读事件
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

int main()
{
    // 地址和端口相关的函数可以封装在InetAddress类中
    const char *ip = "127.0.0.1";
    int port = 8000;
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &addr.sin_addr);
    addr.sin_port = htons(port);

    // listenfd与接收新连接密不可分,与listenfd相关的都可以封装在Acceptor类中
    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    bind(listenfd, (struct sockaddr*)&addr, sizeof(addr));
    listen(listenfd, 5);

    // 与epollfd相关的操作都可以封装在EPollPoller类中
    epoll_event events[MAX_ENVENT_NUM];
    int epollfd = epoll_create(5);
    addfd(epollfd, listenfd);

    // 启动事件循环。何时启动、退出循环的事都可以封装在EventLoop类中
    while (1)  
    {
        int num = epoll_wait(epollfd ,events, MAX_ENVENT_NUM, -1);

        char buf[10];
        memset(buf, '\0', BUFFER_SIZE);
        for (int i = 0; i < num; ++i)
        {
            int sockfd = events[i].data.fd;
            // 如果是listenfd上有可读事件发生,说明有新连接到来
            //(在muduo中,listenfd相关的事件的处理是在主线程中完成的)
            if (sockfd == listenfd)
            {
                // client_addr用于保存客户的地址信息,与地址相关的都可以封装在InetAddress类中
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                // 接收连接,可以封装在Acceptor类中
                int connfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_addr_len);
                // 把新接收的连接connfd注册到epoller中
                addfd(epollfd, connfd);
            }
            // 不是listenfd那就是connfd上的事件
            //(在muduo中,connfd上相关的事件的处理是在子线程中完成的,这里为了简单就没有开启子线程)
            else if (events[i].events & EPOLLIN)   
            {
                // 接收数据。接收数据和发送数据都可以封装在TcpConection类中
                int ret = recv(sockfd, buf, BUFFER_SIZE, 0);
                if (ret <= 0)        // 如果接收数据出错或者对方关闭
                {
                    // 关闭连接。封装在Socket中
                    close(sockfd); 
                }
                // 把接收到的数据发送给对方,也封装在TcpConnection中
                send(sockfd, buf, ret, 0);  
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值