場景描述:
服務端自身FD用的是默認的LT阻塞模式,有新連接時,客戶端的FD設定為非阻塞ET模式。回圈呼叫read,每次讀取3個位元組,設定了EAGAIN錯誤處理,出現該錯誤回傳至epoll_wait()處繼續監聽。
客戶端連接后間隔5秒,每次發送10位元組資料。回圈3次后呼叫close/shutdown關閉連接。設定了SIGINT信號捕捉,收到ctrl + c 組合鍵信號,也會呼叫close/shutdown關閉連接。
server
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#define PORT 8888
#define MAX_CONNECT 10
#define IN_ET_MODE EPOLLIN | EPOLLET
#define OUT_ET_MODE EPOLLOUT | EPOLLET
#define sys_err(ret, str) \
do \
{ \
if (ret == -1) \
{ \
perror(str); \
exit(-1); \
} \
} while (0)
int main(int argc, char const *argv[])
{
int sfd, cfd, epfd, ret, rdlen, i, EventNum;
char buf[BUFSIZ] = {0};
char str[INET_ADDRSTRLEN] = {0};
socklen_t socklen = sizeof(struct sockaddr_in);
struct sockaddr_in srv, clt;
struct epoll_event evt, evts[MAX_CONNECT];
memset(&srv, 0, sizeof(srv));
memset(&clt, 0, sizeof(clt));
memset(&evt, 0, sizeof(evt));
memset(&evts, 0, sizeof(evts));
sfd = socket(AF_INET, SOCK_STREAM, 0);
sys_err(sfd, "socket");
// 1. 創建epoll_event句柄,句柄中含有紅黑數根節點
epfd = epoll_create(10);
sys_err(epfd, "epoll_create");
// 2. 把sfd添加至紅黑樹中
evt.events = EPOLLIN;
evt.data.fd = sfd;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &evt);
sys_err(ret, "epoll_ctl");
//埠復用
int opt = 1;
ret = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
sys_err(ret, "setsockopt");
srv.sin_family = AF_INET;
srv.sin_port = htons(PORT);
srv.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(sfd, (struct sockaddr *)&srv, socklen);
sys_err(ret, "bind");
ret = listen(sfd, 128);
sys_err(ret, "listen");
while (1)
{
printf("epoll_wait()開始監聽事件\n");
EventNum = epoll_wait(epfd, evts, MAX_CONNECT, -1);
sys_err(EventNum, "epoll_wait");
for (i = 0; i < EventNum; i++)
{
if (evts[i].data.fd == sfd) //連接請求
{
printf("有連接請求事件 %d\n", EventNum);
cfd = accept(sfd, (struct sockaddr *)&clt, &socklen);
sys_err(cfd, "accept");
printf("客戶端 %d [%s:%u] 已連接\n",
cfd,
inet_ntop(AF_INET, &clt.sin_addr.s_addr, str, sizeof(str)),
ntohs(clt.sin_port));
//非阻塞
int flag = fcntl(cfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flag);
//添加到epoll_event句柄
evt.events = EPOLLIN | EPOLLET;
evt.data.fd = cfd;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &evt);
sys_err(ret, "epoll_ctl");
}
else //資料請求
{
printf("有資料請求事件 %d\n", EventNum);
int sockfd = evts[i].data.fd;
//printf("客戶端 %d 資料請求:\n",sockfd);
while (1)
{
memset(buf, 0, sizeof(buf));
rdlen = read(sockfd, buf, 3);
//printf("read return %d\n",rdlen);
if (rdlen < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
printf("[EAGAIN] errno = %d, return %d\n", errno, rdlen);
break;
}
else
{
perror("read");
exit(-1);
}
}
else if (rdlen > 0)
{
printf("[RECV] %s, %d\n", buf, rdlen);
int j;
for (j = 0; j < rdlen; ++j)
buf[j] = toupper(buf[j]);
write(sockfd, buf, rdlen);
}
else
{
ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
sys_err(ret, "epoll_ctl");
close(sockfd);
printf("客戶端 %d 斷開連接\n", sockfd);
break;
}
}
}
}
}
return 0;
}
client
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <signal.h>
#define PORT 8888
#define sys_err(ret, str) \
do \
{ \
if (ret == -1) \
{ \
perror(str); \
exit(-1); \
} \
} while (0)
int cfd;
void sigproc(int signo)
{
printf("收到ctrl + C 信號,斷開連接\n");
close(cfd);
//shutdown(cfd, SHUT_RDWR);
exit(-1);
}
int main(int argc, char const *argv[])
{
char buf[BUFSIZ] = {"aaaaaaaaaa"};
struct sockaddr_in srv;
memset(&srv, 0, sizeof(srv));
signal(SIGINT, sigproc);
cfd = socket(AF_INET, SOCK_STREAM, 0);
srv.sin_family = AF_INET;
srv.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &srv.sin_addr.s_addr);
socklen_t len = sizeof(struct sockaddr_in);
int ret = connect(cfd, (struct sockaddr *)&srv, len);
sys_err(ret, "connect");
int n = 3;
while (n--)
{
ret = write(cfd, buf, strlen(buf));
printf("[SEND] %s, %d\n", buf, ret);
sleep(5);
}
//shutdown(cfd, SHUT_RDWR);
close(cfd);
return 0;
}
問題1:
以上情景,無論在客戶端第一次發送資料時發送SIGINTR信號關閉連接,還是回圈完3次呼叫的close或shutdown關閉連接,都不能正常的4次揮手關閉,都是客戶端向服務端發送了RST標志,粗暴的斷開連接。

問題2:
還有,如果客戶端呼叫的是close關閉連接,服務端errno == 104錯誤,Connection reset by peer,相同的位置注釋掉close,使用是shutdown,errno == 11,這個又是為什么?
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/9755.html
標籤:內核源代碼研究區
