socket编程(3-3)

接下来,我们再来实现一个点对点聊天程序。

对等通信,双发都可以发送数据。A可以发数据给B,B也可以发数据给A,通过双方共同维护一个套接字来实现。A维护connA,B维护connB。但是有一个问题,A在接收B发过来的数据的时候(read的时候),他怎么去获得键盘上的输入呢?就我们现在的技术而言,我们只能去在创建一个进程,一个进程用来接收对等方发来的数据,一个进程用来获取键盘输入。对B来说也是一样,也需要有两个进程。所以我们要用多进程的方式来实现。

这个时候,我们实现点对点通信,就不考虑,服务器实现与多个客户端通信的情况了。只考虑一个服务器端与一个客户端通信的情况。

我们假设父进程专门用来获取对方的数据;不需要回射回去

子进程用来发送数据,通过从标准输入获取一行数据,然后把它发送出去(通过conn吧它发送出去)。
就是这样不断的循环,跳出循环我们就把他退出。

服务器端代码:

#include<stdio.h>
#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include<stdlib.h>
#include<errno.h>
#include<string.h>

void handler(int sig)
{
    printf("recv a sig = %d\n",sig);
    exit(EXIT_SUCCESS);
}

int main()
{
    /*create a socket*/
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        printf("socket err");
    
    /*init addr*/    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
    /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/

    int on = 1;/*开启地址重复利用*/
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
        printf("setsockopt err");

    /*将创建的套接字与本地地址进行绑定*/
    if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
        printf("blind err");

    /*监听套接字*/
    if (listen(sockfd,SOMAXCONN) < 0 )
        printf("listen err:");
    
    
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    int conn;
    /*主动套接字*/
    if ((conn = accept(sockfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0 )
        printf("accept err");

    printf("ip = %s,port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));


    pid_t pid;
    pid = fork();//创建一个子进程
    if(pid == -1)
        printf("fork err");

    if (pid == 0 )//子进程,发送数据
    {
        signal(SIGUSR1,handler);//接收信号
        /*发送数据的进程*/
        char sendbuf[1024] = {0};
        while (fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
        {
            write(conn,sendbuf,strlen(sendbuf));
            memset(sendbuf,0,sizeof(sendbuf));
        }
        printf("child close\n");
        exit(EXIT_SUCCESS);
            
    }
    else//父进程,接收数据
    {
        /*接收数据的进程*/
            char recvbuf[1024];
            while(1)
            {

                       memset(recvbuf,0,sizeof(recvbuf));
                       int ret  = read(conn,recvbuf,sizeof(recvbuf));
            if (ret == -1)/*读取错误*/
                printf("read err");
            else if(ret == 0)
            {
                printf("peer close\n");/*对方关闭*/
                break;
            }
                    fputs(recvbuf,stdout);
               }
        printf("parent close\n");
        kill(pid,SIGUSR1);/*向子进程发送一个信号*/
        exit(EXIT_SUCCESS);
    }

    return 0;

}

客户端代码:

#include<stdio.h>
#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>

#include<signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include<stdlib.h>
#include<errno.h>
#include<string.h>

void handler(int sig)
{
    printf("recv a sig = %d\n",sig);
    exit(EXIT_SUCCESS);
}


int main()
{
    /*创建套接字*/    
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        printf("socket err");

    /*serv addr and port*/    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上
      是与服务器端的conn套接字连接*/    
    if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0)
        printf("connect err");
    
    pid_t pid;
    pid = fork();//创建一个子进程
    if (pid == -1)
        printf("fork err");
    if (pid == 0)//子进程,发送数据
    {
        signal(SIGUSR1,handler);//接收信号
        char sendbuf[1024] = {0};
        while (fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
        {
            write(sockfd,sendbuf,sizeof(sendbuf));
            memset(sendbuf,0,sizeof(sendbuf));
        } 
        close(sockfd);
    }
    else//父进程,接收数据
    {
            char recvbuf[1024]={0};
        while (1)
        {
            memset(recvbuf,0,sizeof(recvbuf));    
                    int ret= read(sockfd,recvbuf,sizeof(recvbuf));
            if (ret == -1)
                printf("read err");
            else if(ret == 0)
            {
                printf("peer close \n");
                break;
            }
                    fputs(recvbuf,stdout);
        }
        close(sockfd);
        kill(pid,SIGUSR1);//向子进程发送数据
        exit(EXIT_SUCCESS);

    }

    return 0;

}

父进程退出,子进程是没有退出的。怎么解决呢?我们让父进程退出时,通知到子进程,这可以通过一个方式来实现---信号。输入kill -l 我们来借助一个信号SIGUSR1(用户自定义信号)
父进程向子进程发送信号 kill()函数 ,可以通过man 2 kill 查看

当子进程收到父进程发出的信号,就会执行handler函数。

我们这里实现了使用信号来实现进程间通信。

 

posted @ 2016-12-10 10:42  ren_zhg1992  阅读(90)  评论(0)    收藏  举报