基于TCP的半关闭
在前面的章节中,我们都是通过close或者closesocket来断开套接字连接的,但是调用这两个函数导致我们套接字完全断开,套接字将无法接受数据,并且也只能传输完最后余留在缓冲区的数据内容。此时"只关闭一部分数据交换中使用的流"的方法应运而生。
针对优雅断开的shutdown函数
#include<sys/socket.h>
int shutdown(int sock,int howto);//成功时返回0,失败时返回-1
sock //需要半断开的文件描述符、
howto //进行半断开的方式
此函数的第二个参数可能是下面之一:
1.SHUT_RD//断开输入流
2.SHUT_WR//断开输出流
3.SHUT_RDWR//同时断开IO流
为何需要半关闭
试想一个场景,在客户端和服务端建立连接后,服务端向客户端传递文件,当服务端传递完文件后,客户端需要发送一个"Thank you"给服务端。这里就有一个问题,客户端该何时知道它应该发送“Thank you”给服务端。如果服务端通过close关闭套接字发EOF给客户端的话,服务端将再也无法接受“Thank you”。因此如果服务端只用关闭它的输出流,并且传递EOF给客户端的话就能够解决这个问题。shutdown函数的半关闭可以同时做到上述的两个需求。
基于半关闭的文件传输程序
下面介绍服务器的代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#defind BUF_SIZE 30;
void error_handling(char*message);
int main(int argc,char *argv[]){
int serv_sd,clnt_sd;
FILE *fp;
char buf[BUF_SIZE];
int read_cnt;
struct sockaddr_in serv_addr,clnt_addr;
socklen_t clnt_addr_sz;
if(argc!=2){
printf("Usage: %s <port>\n",argv[0]);
exit(1);
}
fp=fopen("file_server.c","rb");
serv_sd=socket(PF_INET,SOCK_STREAM,0);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_familiy=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
bind(serv_sd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
listen(serv_sd,5);
clnt_addr_sz=sizeof(clnt_addr);
clnt_sd=accept(serv_ad,(struct sockaddr*)&clnt_addr,&clnt_addr_sz);
while(1){
read_cnt=fread((void*)buf,1,BUF_SIZE,fp);
if(read_cnt<BUF_SIZE){
write(clnt_sd,buf,read_cnt);
break;
}
write(clnt_sd,buf,BUF_SIZE);
}
shutdown(clnt_sd,SHUT_WR);
read(clnt_sd,buf,BUF_SIZE);
printf("Message from client: %s \n",buf);
fclose(fp);
close(clnt_sd);close(serv_sd);
return 0;
}
void error_handling(char *message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
下面介绍客户端代码:
#include<"与服务器头文件声明一致,故省略">
#defind BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char*argv[]){
int sd;
FILE *fp;
char buf[BUF_SIZE];
int read_cnt;
struct sockaddr_in serv_addr;
if(argc!=3){
printf("Usage: %s <IP> <port>\n",argv[0]);
exit(1);
}
fp=fopen("receive.dat","wb");
sd=socket(PF_INET,SOCK_STREAM,0);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
connect(sd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
while((read_cnt=read(sd,buf,BUF_SIZE))!=0)
fwrite((void*)buf,1,read_cnt,fp);
puts("Reveived file data");
write(sd,"Thank you",10);
fclose(fp);
close(sd);
return 0;
}
void error_handling(char*message){
//与服务器的内容一致
}
基于Windows的实现
Windows平台同样通过调用shutdown函数完成半关闭,只是想起传递的参数名略有不同。
#include<winsock2.h>
int shutdown(SOCKET sock,int howto);//成功返回0.失败返回SOCKET_ERROR;
sock //要断开的套接字句柄
howto //断开方式的信息
上述函数的第二个参数的可能值及其含义如下:
1.SD_RECEIVE:断开输入流
2.SD_SEND:断开输出流
3.SD_BOTH:断开IO流文章来源:https://www.toymoban.com/news/detail-582635.html
虽然这些常量名不同于Linux中的名称,但是其内部的值完全相同。文章来源地址https://www.toymoban.com/news/detail-582635.html
到了这里,关于TCP/IP 网络编程 第七章:优雅地断开套接字连接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!