Linux下的TCP/IP编程------基于TCP的半关闭

原创 2016年05月31日 11:27:03

在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 mainn(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);
    //接受来自客户端的消息
    read(clent_sock,buff,BUFF_SIZE);
    //打印消息
    printf("Message from client : %s \n",buff);
    //关闭文件
    fclose(fp);
    //彻底关闭TCP连接
    close(clent_sock);
    close(serv_sock);

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){
        //向文件中写入读取到的数据
        fwrite((void *) buff,1,read_count,fp);
    }
    //打印接收到的数据
    puts("Recevied file data");
    //接收完成之后向服务端发送一个消息
    write(sock,"Thank you ",10);

    //关闭文件和socket连接
    fclose(fp);
    close(sock);
    return 0;
}

void error_handling(char * message){
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}

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

版权声明:本文为博主原创文章,转载请注明出处。

相关文章推荐

Linux 进程间通信 --- 信号通信 --- signal --- signal(SIGINT, my_func); --- 按键驱动异步通知

信号  ( signal ) 机制是 UNIX 系统中最为古老的进程间通信机制,很多条件可以产生一个信号. 信号的产生:           1,当用户按下某些按键时,产生信号.    ...

Windows环境下搭建基于Eclipse的STM32 GCC 开发环境(一)工具篇

这段时间玩了一段STM32F207和STM32F407,以前一直用IAR环境,但STM32的库函数太多,码代码实在是费劲,要是有类似VC助手那样的提示功能就好了。上网搜索了一堆资料,貌似Eclipse...

Linux下的TCP/IP编程----socket的可选项

在之前我们都是直接通过socket()函数创建一个socket文件描述符直接使用,其实这只是系统为我们提供的一个默认的socket描述符,我们可以根据自己的工程的需求来对socket进行相应的配置,以...

Linux下的TCP/IP编程----多播和广播的实现

在前边我们实现的都是服务端客户端这样点对点的通讯,数据只是从一个点到另一个点,但是当我们需要将一份数据同时发给指定的多个人时就遇到了问题,总不能建立多个TCP的长连接或者是多个UDP数据报吧。这时我们...

Linux下的TCP/IP编程----IO复用及IO复用服务端

在之前我们实现的并发服务端时通过床将多个进程来实现的,这种并实现并发的方式简单方便,但是进程的创建和销毁是很消耗系统资源的,在访问量大时服务器很容易出现资源不够用的情况。除此之外,由于每个进程有独立的...

利用mtd工具实现嵌入式设备在线升级

主要思路是:通过web post方式将升级文件交给CGI程序处理,然后通过mtd工具实现设备在线升级. 1.页面部分 update.cgi"> 选择升级文件:     这...

设备地址与IO内存映射

在嵌入式编程中,绝大部分功能都是通过驱动外设实现的,这些外设不仅可以是CPU外部的某种功能模块,也可以是CPU芯片内部集成的某些器件。这些芯片内部的外设基本都是通过总线的方式与CPU核心相连,而对它们...

Linux下SRAM的读写

项目中需要用ARM与FPGA进行通信,通信方式是通过FPGA生成的SRAM作为数据的交换区,ARM9芯片为ATMEL的AT91SAM9260,采用Linux 2.6.19的内核,今天搞了一天,总算是读...

嵌入式Linux应用程序访问物理地址的实例

嵌入式Linux应用程序访问物理地址的实例 2013-05-28 14:09:25   来源:EEWORLD    关键字: Linux  驱动  可移植  接口 ...

关于在 Linux 下多个不相干的进程互斥访问同一片共享内存的问题

这里的“不相干”,定义为: 这几个进程没有父子关系,也没有 Server/Client 关系这一片共享内存一开始不存在,第一个要访问它的进程负责新建也没有额外的 daemon 进程能管理这事情 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)