LINUX TCP/IP socket通信3

经过了一个多星期的学习,我在这里总结一下关于Linux,Windows的socket TCP通信的不同情形。

  1. server,client之间的通信
    https://blog.csdn.net/wesigj/article/details/99294538
  2. Socket之TCP全双工Server-Client通信
    https://blog.csdn.net/wesigj/article/details/99322695
  3. client,client之间的通信
  4. 一个小应用-------网络聊天室https://blog.csdn.net/wesigj/article/details/99418596
  5. Linux与Windows之间的socket通信
  6. socket实现两台pc之间的数据传输功能,包括windows到linux,TCP/IP

二 、Socket之TCP全双工Server-Client通信

在上一章中,我们完成了server与client之间的通信,但是是有缺陷的通信。只有发送之后,才能收到对方send的信息,今天,我们来使用fork函数创建一个子进程,其中父进程用来处理发(或者收),而子进程用来处理收(或者发)的过程。
fork函数的使用
#include<unistd.h>
pid_t fork(void);
RETURN VALUE:
On success, the PID of the child process is return int the parent, and 0 is return int child;
On failure -1 is return in the parent, No child process is created,and errno is set approprivately.
一个现有的进程调用fork函数,创建一个新的进程。现有(原有)进程成为新建进程的父进程(父进程),新建进程成为原有进程的子进程(子进程)。父子进程共享正文段,但并不共享存储空间,每个进程有自己独立的数据空间、堆和栈的副本。

client

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<arpa/inet.h>
# include<netinet/in.h>
# include<signal.h>

# define MAX_BUF_LEN 128

/*处理系统调用中产生的错误*/
void error_print(char * ptr)
{
        perror(ptr);
        exit(EXIT_FAILURE);
}
/*处理通信结束时回调函数接收到的信号*/
void quit_tranmission(int sig)
{
    printf("recv a quit signal = %d\n",sig);
    exit(EXIT_SUCCESS);
}
int main(void)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
        error_print("socket");
    struct sockaddr_in servaddr;
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = 1234;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/
    int conn;
    if((conn = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) < 0)
        error_print("connect");

    pid_t pid;
    pid = fork();
    if(pid == -1){
        error_print("fork");
    }

    if(pid == 0){
        char recv_buf[MAX_BUF_LEN] = {0};
        while(1){
            bzero(recv_buf,sizeof(recv_buf));
            int ret = read(sockfd, recv_buf, sizeof(recv_buf));
            if(ret == -1)
                error_print("read");
            else if(ret == 0){
                printf("server is close!\n");
                break;//子进程收到服务器端退出的信息(服务器Ctrl+C结束通信进程,read函数返回值为0,退出循环)
            }
            fputs(recv_buf,stdout);/*将收到的信息输出到标准输出stdout上*/
        }
        close(sockfd);/*子进程退出,通信结束关闭套接字*/
        kill(getppid(),SIGUSR1);/*子进程结束,也要向父进程发出一个信号告诉父进程终止接收,否则父进程一直会等待输入*/
        exit(EXIT_SUCCESS);/*子进程正常退出结束,向父进程返回EXIT_SUCCESS*/
    }
    else{
        signal(SIGUSR1,quit_tranmission);/*回调函数处理通信中断*/
        char send_buf[MAX_BUF_LEN] = {0};
        /*如果服务器Ctrl+C结束通信进程,fgets获取的就是NULL,否则就进入循环正常发送数据*/
        while(fgets(send_buf,sizeof(send_buf), stdin) != NULL){
            int set = write(sockfd, send_buf, strlen(send_buf));/*将send_buf缓冲区的数据发送给对端服务器*/
            if(set < 0)
                error_print("write");
            bzero(send_buf,strlen(send_buf));
        }
        close(sockfd);/*通信结束,关闭套接字*/
    }
    return 0;
}


server

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<arpa/inet.h>
# include<netinet/in.h>
# include<signal.h>

# define MAX_BUF_LEN 128

void error_print(char * ptr)
{
        perror(ptr);
        exit(EXIT_FAILURE);
}

void quit_tranmission(int sig)
{
    printf("recv a quit signal = %d\n",sig);
    exit(EXIT_SUCCESS);
}
int main(void)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);/*IPV4流式协议即TCP协议*/
    if(sockfd < 0)
        error_print("socket");
    struct sockaddr_in servaddr;
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;/*IPV4*/
    servaddr.sin_port = 1234;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");/*使用本地环回地址做测试*/
    /*inet_aton("127.0.0.1",&servaddr.sin_addr);//与inet_addr函数作用相同*/

    /*setsockopt确保服务器不用等待TIME_WAIT状态结束就可以重启服务器,继续使用原来的端口号*/
    int on = 1;
    if( setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
        error_print("setsockopt");
    /*绑定本地Socket地址*/
    if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        error_print("bind");
    /*监听连接*/
    if(listen(sockfd, SOMAXCONN) < 0)
        error_print("listen");

    struct sockaddr_in peeraddr;/*存储连接成功的客户端Socket信息*/
    socklen_t peerlen = sizeof(peeraddr);
    int conn;
    /*接收监听队列第一个完成连接的请求*/
    if((conn = accept(sockfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0)
        error_print("accept");
    pid_t pid;

    pid = fork();/*创建一个新的子进程*/
    if(pid == -1){
        error_print("fork");
    }
    if(pid == 0){/*子进程中用来向客户端发送数据*/
        signal(SIGUSR1,quit_tranmission);/*回调函数处理通信中断*/
        char send_buf[MAX_BUF_LEN]={0};
        /*如果客户端Ctrl+C结束通信进程,fgets获取的就是NULL,否则就进入循环正常发送数据*/
        while(fgets(send_buf, sizeof(send_buf), stdin) != NULL){
            write(conn,send_buf,strlen(send_buf));
            bzero(send_buf,strlen(send_buf));/*发送完成清空发送缓冲区*/
        }
        exit(EXIT_SUCCESS);/*成功退出子进程*/
    }
    else{
        char recv_buf[MAX_BUF_LEN]={0};
        while(1){
            bzero(recv_buf,strlen(recv_buf));
            int ret = read(conn, recv_buf, sizeof(recv_buf));/*读取conn连接发送过来的数据*/
            if(ret < 0)
                error_print("read");
            else if(ret == 0){
                printf("client is close!\n");
                break;//父进程收到服务器端退出的信息(服务器Ctrl+C结束通信进程,read函数返回值为0,退出循环)
            }
            fputs(recv_buf,stdout);
        }
        kill(pid,SIGUSR1);/*父进程结束,也要向子进程发出一个信号告诉子进程终止接收,否则子进程会一直等待输入*/
    }
    close(conn);
    close(sockfd);
    return 0;
}


代码出自这里Apollon_krj


版权声明:本文为CSDN博主「wesigj检」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wesigj/article/details/99294538

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WeSiGJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值