在TCP服务端和客户端建立连接之后服务端和客户端会分别有两个独立的输入流和输出流,而且相互对应。服务端的输出流对应于客户端的输入流,服务端的输入流对应于客户端的输出流。这是在建立连接之后的状态。

  当我们调用close()函数时,系统会同时把双方的输入流和输出流全部关闭,但是有时候我们仍需要在一方断开连接之后只进行接受数据或者传输数据其中一项操作。这时就需要我们只断开输入或者输出,保留另一个流的正常运转,也就引入了TCP的半关闭状态。

基本操作:

之前我们传输完数据之后便直接调用了close()函数,我们可以使用系统提供的shutdown()函数方便的完成TCP的半关闭。

shutdown(int socket , int type):半关闭套接字中的输入或者输出流

  • socket(套接字描述符):需要断开的套接字描述符
  • type(断开类型):套接字的断开方式

  SHUT_RD——断开输入流,并清空输入缓冲中的数据

  SHUT_WR——断开输出流,并将输出缓冲中的数据输出

  SHUT_RDWR——同时断开输入输出流,分两次调用shutdown()函数

  成功时返回0,失败时返回-1

服务端:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h> #define BUFF_SIZE 30 void error_handling(char *message); /**
基于TCP的半连接服务端
**/
int main(int argc ,char *argv[]){
//客户端和服务端的socket描述符
int serv_sock;
int clent_sock;
//客户端和服务端的地址
struct sockaddr_in serv_addr;
struct sockaddr_in clent_addr;
//客户端地址大小
socklen_t client_addr_size;
//用于记录读取的文件字节数
int read_count;
//缓冲大小
char buff[BUFF_SIZE];
//用于打开文件的文件指针
FILE *fp;
//检查输入的参数个数是否合法
if(argc!=2){
printf("Usage : %s <port> \n ",argv[0]);
exit(1);
} //使用文件描述符打开文件
fp = fopen("word_file.txt","rb");
//创建服务端saocket
serv_sock = socket(AF_INET,SOCK_STREAM,0);
//将server_addr中的内容清空
memset(&serv_addr,0,sizeof(serv_addr));
//初始化socket地址
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1])); //进行地址绑定
if(bind(serv_sock,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) == -1){
error_handling("bind() error");
}
//使服务端进入监听状态
if(listen(serv_sock,5) == -1){
error_handling("listen() error");
}
//获的客户端地址的大小
client_addr_size = sizeof(clent_addr);
clent_sock = accept(serv_sock,(struct sockaddr *) &clent_addr,&client_addr_size); while(1){
//读取的文件字节数
read_count = fread((void *) buff,1,BUFF_SIZE,fp);
//向客户端写入数据
if(read_count<BUFF_SIZE){
write(clent_sock,buff,read_count);
break;
}
write(clent_sock,buff,BUFF_SIZE);
}
/**
在数据输出完成之后,对输出流进行流半关闭
这种状态下服务读不能向客户端写入数据,但是可以接受来自客户端的数据
**/
shutdown(clent_sock,SHUT_WR);
//接受来自客户端的消息
while(0 == read(clent_sock,buff,BUFF_SIZE))
{
continue;
}
//打印消息
printf("Message from client : %s \n",buff);
//关闭文件
fclose(fp);
write(clent_sock,"11111",5);
//彻底关闭TCP连接
//close(clent_sock);
//close(serv_sock);
//避免因进程退出,导致 server 向 client 发 FIN
while(1){};
return 0;
} void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}

   客户端:

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h> #define BUFF_SIZE 30 void error_handling(char *message); int main(int argc ,char *argv[]){
int sock;
FILE *fp; char buff[BUFF_SIZE];
int read_count;
struct sockaddr_in server_addr; if(argc!=3){
printf("Usage : %s <port> \n ",argv[0]);
exit(1);
} fp = fopen("recevice.dat","wb");
sock = socket(AF_INET,SOCK_STREAM,0); memset(&server_addr,0,sizeof(server_addr)); //初始化socket地址
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2])); connect(sock,(struct sockaddr *) &server_addr,sizeof(server_addr)); while(read_count = read(sock,buff,BUFF_SIZE) != 0){
//向文件中写入读取到的数据
printf("recv data from server : %s\n",buff);
fwrite((void *) buff,1,read_count,fp);
}
//打印接收到的数据
puts("Recevied file data");
//接收完成之后向服务端发送一个消息
// 睡3秒,确保server端 shutdown()已执行
sleep(3);
write(sock,"Thank you ",10);
write(sock,"server",6); //关闭文件和socket连接
fclose(fp);
// 避免因进程退出,导致 client 向 server 发 FIN
while(1){};
//close(sock);
return 0;
} void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}

  

  至此就完成了一个基于半关闭的TCP服务端/客户端程序,这样使得服务端在传输完数据之后可以只保留输入流,关闭输出流。

抓包:https://files.cnblogs.com/files/yorkyang/tcp%E5%8D%8A%E5%85%B3%E9%97%AD.zip

转自:http://blog.csdn.net/wqc_csdn/article/details/51543504

TCP/IP编程——基于TCP的半关闭的更多相关文章

  1. linux tcp/ip编程和windows tcp/ip编程差别以及windows socket编程详解

    最近要涉及对接现有应用visual c++开发的tcp客户端,花时间了解了下windows下tcp开发和linux的差别,从开发的角度而言,最大的差别是头文件(早期为了推广尽可能兼容,后面越来越扩展, ...

  2. 网络编程(二)——TCP协议、基于tcp协议的套接字socket

    TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...

  3. Java 网络编程 -- 基于TCP 模拟多用户登录

    Java TCP的基本操作参考前一篇:Java 网络编程 – 基于TCP实现文件上传 实现多用户操作之前先实现以下单用户操作,假设目前有一个用户: 账号:zs 密码:123 服务端: public c ...

  4. TCP/IP笔记(二)TCP/IP简介

    上回,主要介绍了下协议和OSI参考模型,并简单了解下网络构成要素,这回该说说TCP/IP了 互联网与TCP/IP的关系   互联网进行通信时,需要相应的网络协议,TCP/IP原本就是为使用互联网而开发 ...

  5. TCP/IP 协议图--TCP/IP 基础

    1. TCP/IP 的具体含义 从字面意义上讲,有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议.实际生活当中有时也确实就是指这两种协议.然而在很多情况下,它只是利用 IP 进行通信时所 ...

  6. tcp/ip学习笔记-TCP

    tcp/ip学习笔记-TCP 彭会锋 报文发送采用的是tcp_output函数,

  7. TCP/IP协议图--TCP/IP基础

    1. TCP/IP 的具体含义 从字面意义上讲,有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议.实际生活当中有时也确实就是指这两种协议.然而在很多情况下,它只是利用 IP 进行通信时所 ...

  8. OSI参考模型与TCP/IP参考模型与TCP/IP协议栈

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11484126.html OSI参考模型与TCP/IP参考模型与TCP/IP协议栈 TCP/IP分层模型 ...

  9. 20181225 基于TCP/IP和基于UDP/IP的套接字编程

    一.TCP/IP的套接字编程 服务器端代码: import  socket​server = socket.socket() # 默认是基于TCP# 基于TCP的对象serve=socket.sock ...

随机推荐

  1. Selenium基础知识(二)鼠标操作

    一.鼠标操作 这个需要使用webdriver下的ActionChains类,这个类是操作鼠标操作的: from selenium.webdriver import ActionChains 鼠标操作可 ...

  2. 把Web Services生成服务器端C#、VB。或者接口

    什么是Web Services? Web Services 是应用程序组件 Web Services 使用开放协议进行通信 Web Services 是独立的(self-contained)并可自我描 ...

  3. iframe中父页面与子页面的传值方法

    涉及到iframe传值的情况有这么几种:(1)父页面给iframe中的子页面传值(2)子页面调用父页面中的函数(3)iframe中的子页面给父页面传值(4)兄弟iframe之间的传值 下面来逐一看一下 ...

  4. docker tool

    安装docker tool http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/ 安装1.8.3 有把vbox等服务器地址加 ...

  5. tfs团队项目删除原来连接的默认账户

    1.在用visual studio 连接团队项目时,首次输入用户名和密码后,默认保存住凭据了,等以后连接会自动采用首次的凭证. 但是如何采用新的用户重新登录呢.如图所示,删除原有的凭证.删除后重启电脑 ...

  6. RabbitMQ理论

    RabbitMQ理论   消息 = 有效载荷(数据) + 标签(包含载荷和收件人的信息)   信道:你的应用于RabbitMQ代理服务器之间的TCP连接(有唯一的ID),信道主要解决了每一个线程单独T ...

  7. BFS 搜索 蓝桥杯模拟赛

    题目链接:https://nanti.jisuanke.com/t/36117 这个题目想不到用广搜来做,一直在想深搜. 广搜的思路呢,是把最外圈不是黑色(不是0)的数 的位置 i 和 j 进队,赋值 ...

  8. [转]js刷新父窗体

    浮层内嵌iframe及frame集合窗口,刷新父页面的多种方法   <script language=JavaScript>       parent.location.reload(); ...

  9. Vue系列之 => 全局,私有过滤器

    私有过滤器也称局部过滤器 <script> // 全局过滤器 Vue.filter("datatime",function(timestr){ var tm = new ...

  10. GCD(莫比乌斯+去重)

    题目链接 莫比乌斯反演模板题, 去重即可: 我们可以发现只有在区间重叠部分才会有重复且为cal(e, e, k)/2;(e表示b, d中较小的一个): #include<cstdio> # ...