本来想测试一些东西,很久没写了,竟然被某个细节卡住了。还得特意翻别人的博客才知道。还是写一下,下次翻自己的,哈哈
一、 代码实现(有详细注释):
epollSocket.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <string.h>
#include <arpa/inet.h>
#define MAXLINE 128
#define SERV_PORT 8001
#define MAXEVENTS 50
int createListen(int argc, char *argv[]){
char *ip = NULL;
int port = 0;
if(argc == 3){
ip = argv[1];
port = atoi(argv[2]);
}
// 1. 创建 socket(参数:协议族ipv4, socket连接类型, 协议类型)
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if(socketfd == -1){
perror("crate socket err1\n");
return -1;
}
// 2. 创建并初始化 监听端sockaddr(协议族, ip, port端口号)
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, !ip ? "0.0.0.0" : ip, &servaddr.sin_addr);
printf("argv[0]:%s", argv[0]);
printf("port:%d", port ? port : SERV_PORT);
servaddr.sin_port = htons(port ? port : SERV_PORT);
// 3. 绑定监听端sockaddr(参数:监听端 socketfd, 监听端socketaddr指针, 监听端socketaddr字节数))
bind(socketfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
// 4. 设置监听个数
listen(socketfd, 5);
return socketfd;
}
static int
make_socket_non_blocking (int sfd)
{
int flags, s;
//得到文件状态标志
flags = fcntl (sfd, F_GETFL, 0);
if (flags == -1)
{
perror ("fcntl");
return -1;
}
//设置文件状态标志
flags |= O_NONBLOCK;
s = fcntl (sfd, F_SETFL, flags);
if (s == -1)
{
perror ("fcntl");
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int listenfd = createListen(argc, argv);
if(listenfd == -1){
perror("crate socket err1\n");
return -1;
}
int ret = make_socket_non_blocking(listenfd);
if(ret == -1){
perror("make_socket_non_blocking err1\n");
return -1;
}
// 5. 创建发送端sockaddr
struct sockaddr_in clientaddr;
int addlen = sizeof(clientaddr);
printf("start epoll_create1\n");
int epfd = epoll_create1(0);
if(epfd == -1){
perror("epoll_create1");
return -1;
}
struct epoll_event event;
struct epoll_event *eventList;
event.data.fd = listenfd;
event.events = EPOLLIN | EPOLLET;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &event);
if(ret != 0){
perror("epoll_ctl");
return -1;
}
eventList = (struct epoll_event *)calloc(MAXEVENTS,sizeof(struct epoll_event));
printf("start while\n");
char buf[MAXLINE];
while(1){
int i, n;
printf("start epoll_wait\n");
n = epoll_wait(epfd, eventList, MAXEVENTS, -1);
printf("epoll_wait,len:%d\n", n);
for(i = 0; i < n; i++){
if ((eventList[i].events & EPOLLERR) ||
(eventList[i].events & EPOLLHUP) ||
(!(eventList[i].events & EPOLLIN)))
{
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
fprintf (stderr, "epoll error\n");
close (eventList[i].data.fd);
continue;
}else if(listenfd == eventList[i].data.fd){
// 6. 接受发送端(没有发送端连接,会阻塞)
// 参数: 监听端 socketfd, 发送端sockaddr指针,发送端sockaddr字节数指针
// 返回 发送端socketfd,
//注意:这里第三个参数用的指针,为了方便返回字节数
printf("开始连接发送端\n");
int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, (socklen_t *)&addlen);
if(clientfd == -1){
perror("accept");
return -1;
}
ret = make_socket_non_blocking(clientfd);
if(ret == -1){
perror("make_socket_non_blocking");
return -1;
}
printf("开始连接发送端%d\n", clientfd);
event.events = EPOLLET | EPOLLIN;
event.data.fd = clientfd;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &event);
if(ret == -1){
perror("epoll_ctl");
return -1;
}
}else{
int clientfd = eventList[i].data.fd;
printf("开始接收发送端%d\n", clientfd);
// 7. 开始 接收发送数据
char sendbuf[] = "sockservicerecive\n";
int done = 0;
while(1){
// 读取数据|参数:发送端socketfd, 数据指针,数据字节数
int count = read(clientfd, buf, MAXLINE);
if (count == -1)
{
/* 如果errno==EAGAIN,则表示我们已读取所有数据。回到主循环。 */
if (errno != EAGAIN)
{
perror ("read");
done = 1;
}
break;
}
else if (count == 0)
{
/* 文件结束。遥控器已关闭连接。 */
done = 1;
break;
}
/* 将缓冲区写入标准输出 */
ret = write (1, buf, count);
if (ret == -1)
{
perror ("write");
abort ();
}
}
if (done)
{
printf ("Closed connection on descriptor %d\n",
eventList[i].data.fd);
/* 关闭描述符将使epoll从监视的描述符集中删除它。*/
close (eventList[i].data.fd);
}
}
}
}
// 8. 关闭连接
close(epfd);
return 0;
}
二、调用
生成并执行文件
g++ ./epollSocket.cpp
./a.out
客户端连接
新开一个终端
telnet 127.0.0.1 8001
#连接成功后即可发送数据