测试服务器程序:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/uio.h>
#include <fcntl.h>
#define EVENT_NUM 1
//设置套接字为非阻塞
int set_fd_nonblock(int fd)
{
int val;
int res;
val = fcntl(fd,F_GETFL,0);
if(val == -1)
{
printf("%d\n;%s",errno,strerror(errno));
return -1;
}
res = fcntl(fd,F_SETFL,(val | O_NONBLOCK));
if(res == -1)
{
printf("%d\n;%s",errno,strerror(errno));
return -1;
}
}
int run = 1;
int readBuf(int fd,char* buf,size_t buf_len);
int main()
{
size_t read_size;
int res;
int client_fd;
int fd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in server_addr,client_addr;
int n;
socklen_t len;
server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
server_addr.sin_port = htons(5000);
server_addr.sin_family = AF_INET;
//绑定端口
res = bind(fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(res < 0)
{
printf("errno:%d,errno:%s\n",errno,strerror(errno));
exit(-1);
}
//监听端口
res = listen(fd,60);
if(res < 0)
{
printf("errno:%d,errno:%s;file:%s;line:%d\n",errno,strerror(errno),__FILE__,__LINE__);
exit(-1);
}
char buf[BUFSIZ];
//计数器
int count = 0;
int i = 0;
struct epoll_event event;
//集合
struct epoll_event eventCollect[EVENT_NUM];
//创建一个epoll集合
int epoll_fd = epoll_create(EVENT_NUM);
int file_fd;
unlink("cache2.log");
file_fd = open("cache2.log",O_WRONLY|O_CREAT,S_IRWXU);
printf("file_fd:%d\n",file_fd);
//将监听套接字加入
event.data.fd = fd;
event.events = EPOLLIN|EPOLLET;
//把监听的套接字加入集合
res = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,fd,&event);
while(run)
{
n = epoll_wait(epoll_fd,eventCollect,3,-1);
if(n < 0)
{
if(errno == EINTR)
{
continue;
}
}else if(n > 0)
{
for(i = 0;i<n;i++)
{
//server
if(eventCollect[i].data.fd == fd)
{
len = sizeof(client_addr);
client_fd = accept(eventCollect[i].data.fd,(struct sockaddr *)&client_addr,&len);
if(client_fd < 0)
{
continue;
}else{
bzero(&event,sizeof(event));
event.data.fd = client_fd;
event.events = EPOLLIN|EPOLLET;
res = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,client_fd,&event);
set_fd_nonblock(client_fd);
//shutdown(client_fd, SHUT_RDWR);
}
}else{
//client
client_fd = eventCollect[i].data.fd;
bzero(buf,sizeof(buf));
// read_size = recv(client_fd,buf,sizeof(buf),0);
while((read_size = readBuf(client_fd,buf,sizeof(buf)))) {
if (read_size == -1) {
if (errno == EINTR) {
continue;
} else if(errno == EAGAIN) {
break;
}else{
printf("errno:%d,errno:%s\n", errno, strerror(errno));
}
} else if (read_size > 0) {
//数据报可能出现粘包问题
//查看接收结果
// write(file_fd,buf,sizeof(buf));
count += read_size;
int res = write(file_fd,buf,read_size);
printf("%d\n",res);
bzero(buf,read_size);
}
}
if(read_size == 0) {
//客户端套接字已经关闭了
printf("close;size:%d\n", count);
count = 0;
close(client_fd);
break;
}
}
}
}
}
return 0;
}
int readBuf(int fd,char* buf,size_t buf_len)
{
struct iovec iov[1];
ssize_t n;
iov[0].iov_base = buf;
iov[0].iov_len = buf_len;
return readv(fd,iov,1);
}
client程序:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
int main()
{
int socket_fd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(socket_fd,(struct sockaddr*)&addr,sizeof(addr));
if(res < 0)
{
printf("%d;%s\n",errno,strerror(errno));
exit(-1);
}
printf("%d\n", socket_fd);
sleep(1000);
shutdown(socket_fd, SHUT_RDWR);
// close(socket_fd);
socket_fd = socket(AF_INET,SOCK_STREAM,0);
printf("%d\n", socket_fd);
return 0;
}
总结:
总结一下:
1、shutdown 不会关闭掉fd,但是会忽略fd的计数器,直接关闭掉链接
2、close 会减少fd计数器,当为0的时候会close掉链接和fd
3、close调用后会直接从epoll中删除,不会触发epoll事件
4、shutdown后会触发epoll事件
5、close会直接丢弃缓冲区
5、shutdown是会冲刷缓冲区的