了解close与shutdown区别,我们先回忆下文件描述符与进程的关系。代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *pathname = "a.txt";
char father[] = "this isffffffffffffffffif father";
char son[] = "this is sssssssssssssssssssssssssss son";
int fd;
int re;
fd = open(pathname,O_CREAT|O_RDWR);
pid_t pid = fork();
if(pid > 0)
{
write(fd,father,sizeof(father));
re = close(fd);
printf("father re = %d \n",re);
re = close(fd);
printf("father re 2 = %d \n",re);
}else if(pid == 0)
{
sleep(1);
write(fd,son,sizeof(son));
re = close(fd);
printf("son re = %d \n",re);
}
}
多进程中,父子进程都会有独立的空间(PCB,代码段,数据段,BSS,还有堆栈段),但是这时候文件描述符是共用的,都可以往同一个文件写内容。但是引入了一个计数器。如下图:refcnt:2,我们必须close两次才能把文件彻底关掉。
了解了如上内容,我们可以继续往下:
1.server代码如下:收到第一个字符为 1 后 close 当前进程文件描述符,关注下最后一行。server收数据
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
#include<signal.h>
void handler(int num)
{
//wait(NULL);
int pp_pid = 0;
while((pp_pid = waitpid(-1,NULL,WNOHANG)) > 0)
{
printf("break pid :%d \n",pp_pid);
}
exit(0);
}
int main()
{
struct sockaddr_in addr;
struct sockaddr_in addr_peer;
struct in_addr ip;
inet_aton("127.0.0.1",&ip);
addr.sin_family = AF_INET;
addr.sin_port =htons(8001);
addr.sin_addr = ip;
int sockfd = 0;
int conn =0;
int re = 0;
char buf[1024] = {0};
char buf_write[1024] = {0};
signal(SIGCHLD,handler);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("sockfd error \n");
}
re = bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in));
if(re == -1)
{
perror("bind error");
}
conn = listen(sockfd,SOMAXCONN);
if(conn == -1)
{
perror("listen error \n");
}
//while(1)
{
int len = sizeof(struct sockaddr_in);
conn =accept(sockfd,(struct sockaddr *)&addr_peer,&len);
printf("port:%u \n",ntohs(addr_peer.sin_port));
pid_t pid = fork();
if(pid == 0)
{
int ret = 0;
while(1)
{
ret = read(conn,buf,sizeof(buf));
if(buf[0] == '1')
{
close(conn);
}
fputs(buf,stdout);
memset(buf,0,sizeof(buf));
}
}else if(pid > 0)
{
while(fgets(buf_write,sizeof(buf_write),stdin))!= NULL)
{
write(conn,buf_write,sizeof(buf_write));
}
}
}
client代码如下:client负责读写数据:
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<signal.h>
#include<string.h>
void handler(int num)
{
printf("num = %d \n",num);
}
int main()
{
struct sockaddr_in addr;
struct in_addr ip;
inet_aton("127.0.0.1",&ip);
addr.sin_family = AF_INET;
addr.sin_port =htons(8001);
addr.sin_addr = ip;
int re = 0,i = 0,conn =0,sockfd = 0;
char buf[1024] ={0};
char buf_read[1024] = {0};
signal(SIGPIPE,handler);
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("sockfd error \n");
}
conn = connect(sockfd, (const struct sockaddr *)&addr,sizeof(struct sockaddr_in));
if(conn == -1)
{
perror("conn error \n");
}
}
pid_t pid = fork();
if(pid == 0)
{
while((fgets(buf,sizeof(buf),stdin)) != NULL)
{
write(sockfd,buf,sizeof(buf));
memset(buf,0,sizeof(buf));
}
}else if(pid > 0)
{
while(1)
{
re = read(sockfd,buf_read,sizeof(buf_read));
if(re == 0)
{
close(sockfd);
printf("peer is break \n");
}
fputs(buf_read,stdout);
memset(buf_read,0,sizeof(buf_read));
}
}
}
此时client发送1 后,server收到后关闭文件描述符,但是看状态:
tcp 0 0 localhost:49021 localhost:8001 ESTABLISHED
tcp 3072 0 localhost:8001 localhost:49021 ESTABLISHED
此时只是关闭了client的写和servser可以的读。server发数据client仍然可以读到。只有两个进程都关闭了文件描述符,才会发FIN字段,开始断开的4次握手。
close了解了
开始了解下shutdown。
SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。进程将不能对该
套接字发出任何读操作。对TCP套接字该调用之后接受到的任何数据将被确认然后无声的丢弃掉。
SHUT_WR:关闭连接的写端,进程不能在对此套接字发出写操作
SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR
使用close中止一个连接,但它只是减少描述符的参考数,并不直接关闭连接,只有当描述符的参考数为0时才关闭连接。
shutdown可直接关闭描述符,不考虑描述符的参考数,可选择中止一个方向的连接。
//更改server代码,将close改为shutdown
while(1)
{
ret = read(conn,buf,sizeof(buf));
if(buf[0] == '1')
{
//close(conn);
shutdown(conn,SHUT_RDWR);
}
fputs(buf,stdout);
memset(buf,0,sizeof(buf));
}
查看状态
tcp 0 0 localhost:8001 localhost:49024 FIN_WAIT2
tcp 0 0 localhost:49024 localhost:8001 CLOSE_WAIT