并发通信

采用TCP协议的CS架构:

       服务器使用了多进程,创建了三个进程:父进程用来检测客户端连接情况,子进程用来接收客户端消息,孙进程用来向连接上来的客户端发送“hello”。

      为优化代码结构,服务器中的接收消息和发送消息都是使用了execl函数封装。

      客户端使用了多线程,主线程用来向服务器发送消息,次线程用来接收服务器的问候。

遇到的问题:客户端强制退出时,服务器端将出现发送异常抛出SIGPIPE;

解决方法:在发送函数里使用signal信号函数将SIGPIPE信号忽略,并且结束进程。

备注:只是简单学习并发通信,并没有对代码进行细致优化,存在一些bug。

服务器代码:

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

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>

#include <unistd.h>
#include <error.h>

#define PORT 8888

int main()
{
    int sockfd;
    int cfd;
    int ret;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int clen;
    char buf[1024];
    pid_t pid;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    printf("socket success!\n");

    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.16");

    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); //(struct sockaddr *)&
    if (ret == -1)
    {
        perror("bind");
        exit(1);
    }
    printf("bind success!\n");

    ret = listen(sockfd, 5);
    if (ret == -1)
    {
        perror("listen");
        exit(1);
    }
    printf("listen success!\n");

    clen = sizeof(client_addr);

    while (1)
    {
        bzero(&client_addr, sizeof(client_addr));
        cfd = accept(sockfd, (struct sockaddr *)&client_addr, &clen); 
        if (cfd == -1)
        {
            perror("accept");
            exit(1);
        }
        printf("accept success!\n");

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

        //要有三个进程, 父进程等待客户端连接,子进程接收客户端消息,孙进程持续向客户端发送字符串
        pid = fork();
        if (pid < 0)
        {
            perror("fork");
            exit(1);
        }
        else if (pid > 0)
        {
            close(cfd);  //子进程已经获得cfd,父进程继续等待连接
        }

        else
        {
            char cfd_buf[1024];  //存放重定向的cfd
            memset(cfd_buf, 0, sizeof(cfd_buf));
            sprintf(cfd_buf, "%d", cfd); //将cfd按照整形转成字符串重定向到cfd_buf里
                                         //之后要用atoi()将字符串转成整数
            pid_t pid2 = fork();
            if (pid2 < 0)
            {
                perror("fork2");
                exit(1);
            }
            else if (pid2 > 0)
            {
                execl("./recv_msg", "./recv_msg", cfd_buf, NULL);
            }
            else
            {
                execl("./send_msg", "./send_msg", cfd_buf, NULL);
            }
        }
    }
    close(sockfd);

    return 0;
}

服务器发送消息send_msg():

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

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

int main(int argc, char **argv)
{
    int ret;
    int cfd = atoi(argv[1]);  //将字符串转成整数,
    while (1)
    {
        ret = send(cfd, "hello", strlen("hello"), 0);
        signal(SIGPIPE, SIG_IGN);
        if (ret < 0)
        {
            perror("send");
            _exit(1);
        }
        else if (ret == 0)
        {
            printf("%d is exit!\n", cfd);
            _exit(1);
        }
        else
        {
            sleep(2);
        }
    }

}

 服务器接收消息recv_msg():

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

int main(int argc, char*argv[])
{
    int ret;
    char buf[1024];
    int cfd = atoi(argv[1]);

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        ret = recv(cfd, buf, sizeof(buf), 0);  //0阻塞接收
        if (ret < 0)
        {
            perror("recv");
            exit(1);
        }
        else if (ret == 0)
        {
            printf("%d is exit!(child)\n", cfd);
            shutdown(cfd, SHUT_RDWR);
            _exit(1);
        }
        else
        {
            printf("recv : %s\n", buf);
        }
    }
    

    return 0;
}

 客户端代码:

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

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

#define PORT 8888

void *recv_msg(void *arg)
{
    int ret;
    char buf[1024];

    int cfd = *((int *)arg);
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        ret = recv(cfd, buf, sizeof(buf), 0);
        if (ret < 0)
        {
            perror("recv");
            exit(1);
        }
        else if (ret == 0)
        {
            printf("%d is exit\n", cfd);
            pthread_exit(NULL);
        }
        else
        {
            printf("client recv: %s\n", buf);
        }
    }
    pthread_exit(NULL);
}


int main()
{
    int sockfd;
    int ret;
    char buf[1024];
    pthread_t thread;

    struct sockaddr_in server_addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket");
        exit(1);
    }
    printf("socket success!\n");

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.16");

    ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (ret == -1)
    {
        perror("connect");
        exit(1);
    }

    printf("connect success!\n");

    if (pthread_create(&thread, NULL, recv_msg, (void *)&sockfd) != 0)
    {
        perror("pthread create");
        exit(1);
    }

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        printf("please input send message:\n");
        gets(buf);
        send(sockfd, buf, strlen(buf), 0);
    }

    shutdown(sockfd, SHUT_RDWR);

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值