【计算机网络】3. 网络编程socket2(多线程tcpsocket、多进程tcpsocket、)

1. 多线程TCPsocket编程

在这里插入图片描述

1.1 服务端

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

struct ThreadInfo
{
    int newsockfd_;

};

void* TcpThreadStart(void* arg)
{
    pthread_detach(pthread_self());
    struct ThreadInfo* ti = (struct ThreadInfo*)arg;
    int newsockfd = ti->newsockfd_;

    while(1)
    {
        //接收
        char buf[1024] = {0};
        ssize_t recv_size = recv(newsockfd, buf, sizeof(buf) - 1, 0);
        if(recv_size < 0)
        {
            perror("recv");
            continue;
        }
        else if(recv_size == 0)
        {
            printf("peer close connect\n");
            close(newsockfd);
            break;
        }

        printf("%s\n", buf);

        memset(buf, '\0', sizeof(buf));
        strcpy(buf, "i am server!!!");
        send(newsockfd, buf, strlen(buf), 0);
    }

    delete ti;
    return NULL;
}

int main()
{
    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sock < 0)
    {
        perror("socket");
        return 0;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(28989);
    //0.0.0.0 : 本地所有的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");//私网ip

    int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("bind");
        return 0;
    }

    ret = listen(listen_sock, 1);
    if(ret < 0)
    {
        perror("listen");
        return 0;
    }

    while(1)
    {
        struct sockaddr_in cli_addr;
        socklen_t cli_addrlen = sizeof(cli_addr);
        int newsockfd = accept(listen_sock, (struct sockaddr*)&cli_addr, &cli_addrlen);
        if(newsockfd < 0)
        {
            perror("accept");
            return 0;
        }

        printf("accept new connect from client %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));

        struct ThreadInfo* ti = new ThreadInfo;
        ti->newsockfd_ = newsockfd;
        //创建线程
        pthread_t tid;
        ret = pthread_create(&tid, NULL, TcpThreadStart, (void*)ti);
        if(ret < 0)
        {
            close(newsockfd);
            delete ti;
            continue;
        }
    }

    close(listen_sock);
    return 0;
}

1.2 客户端

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

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockfd < 0)
    {
        perror("socket");
        return 0;

    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(28989);
    // 0.0.0.0 : 本地所有的网卡地址
    addr.sin_addr.s_addr = inet_addr("82.157.94.99");//必须使用公网ip

    int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("connect");
        return 0;
    }


    while(1)
    {

        char buf[1024] = "i am client1111222";

        send(sockfd, buf, strlen(buf), 0);

        memset(buf, '\0', sizeof(buf));
        //接收
        ssize_t recv_size = recv(sockfd, buf, sizeof(buf) - 1, 0);
        if(recv_size < 0)
        {
            perror("recv");
            continue;
        }
        else if(recv_size == 0)
        {
            printf("peer close connect\n");
            close(sockfd);
            continue;
        }

        printf("%s\n", buf);

        sleep(1);
    }

    close(sockfd);
    return 0;
}

在这里插入图片描述

1.3 常见问题

(1)服务端的侦听端口,如果udp已经绑定了某一个端口,tcp是否可以再次绑定?
一个端口同时可以被udp程序和tcp程序所绑定,在网络层已经能够区分一个网络数据是传输给传输层的tcp协议还是udp协议
(2)telnet测试tcp端口是否开放(是否在监听),引申含义:服务端是否正常工作?
可以使用telnet模拟tcp的客户端,给tcp的服务端发送建立连接的请求,也可以模拟三次握手的过程,建立tcp连接
在这里插入图片描述

(3)关于backlog
记住:能够和服务端建立的连接数量 = backlog + 1

2. 多进程TCPsocket编程

在这里插入图片描述
想要让子进程与客户端进行通信,则子进程当中一定要存在新连接的套接字,否则子进程就没有办法和客户端进行通信
结论:一定是父进程接收新连接完毕之后,再fork子进程

2.1 服务端

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

void sigcallback(int signo)
{                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
    printf("recv signo : %d\n", signo);
    //当前的wait是进程等待的阻塞接口, 但是应用的场景一定是子进程退出之后,
    //父进程收到了SIGCHLD信号之后, 才会回调sigcallback函数, 才会调用wait
    wait(NULL);
}

int main()
{
    signal(SIGCHLD, sigcallback);
    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sock < 0)
    {
        perror("socket");
        return 0;

    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(28989);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("bind");
        return 0;

    }

    ret = listen(listen_sock, 5);
    if(ret < 0)
    {
        perror("listen");
        return 0;

    }


    while(1)
    {
        int new_sock = accept(listen_sock, NULL, NULL);
        if(new_sock < 0)
        {
            continue;

        }

        //创建子进程
        int pid = fork();
        if(pid < 0)
        {
            //创建子进程失败, 但是接受新连接成功
            close(new_sock);
            continue;
        }
        else if(pid == 0)
        {
            //child
            close(listen_sock);
            while(1)
            {
                //recv   and      send
                char buf[1024] = {0};

                ssize_t recv_size = recv(new_sock, buf, sizeof(buf) - 1, 0);
                if(recv_size < 0)
                {
                    perror("recv");
                    continue;
                }
                else if(recv_size == 0)
                {
                    printf("peer shutdown!!\n");
                    close(new_sock);

                    //子进程退出掉 -- TODO
                    exit(1);
                }

                printf("client say: \"%s\"\n", buf);

                memset(buf, '\0', sizeof(buf));
                strcpy(buf, "i am server");
                send(new_sock, buf, strlen(buf), 0);
            }
        }
        else
        {
            //father
            close(new_sock);
        }
    }
    return 0;
}

2.2 客户端

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

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockfd < 0)
    {
        perror("socket");
        return 0;

    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(28989);
    // 0.0.0.0 : 本地所有的网卡地址
    addr.sin_addr.s_addr = inet_addr("82.157.94.99");//必须使用公网ip

    int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("connect");
        return 0;
    }


    while(1)
    {

        char buf[1024] = "i am client1111222";

        send(sockfd, buf, strlen(buf), 0);

        memset(buf, '\0', sizeof(buf));
        //接收
        ssize_t recv_size = recv(sockfd, buf, sizeof(buf) - 1, 0);
        if(recv_size < 0)
        {
            perror("recv");
            continue;
        }
        else if(recv_size == 0)
        {
            printf("peer close connect\n");
            close(sockfd);
            continue;
        }

        printf("%s\n", buf);

        sleep(1);
    }

    close(sockfd);
    return 0;
}

父进程对于子进程退出的处理是方式默认处理,即忽略不处理,这在子进程退出时会导致大量的僵尸进程
当打开一个客户端,ctrl+c掉,再打开另外一个客户端,继续ctrl+c掉,这样就会出现两个僵尸进程,当打开的客户端较多时,造成较多的僵尸进程会占用系统太多的资源

在这里插入图片描述
解决:改变父进程对子进程退出时的处理方式

signal(SIGCHLD, sigcallback);

void sigcallback(int signo)
{                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
    wait(NULL);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值