socket网络编程之TCP、UDP

发布时间:2020-07-15 04:50:04 作者:SherryX
来源:网络 阅读:1971

之前说的用于进程间通信的几种方式:消息signal、管道pipe、消息队列msg、共享内存shm、信号量sem。都只适用于一台主机上的进程间通信,那么如何实现两台计算机之间的进程通信呢?所以,来了解一下异地进程通信

1 异地进程通信

client.c

#include <sys/types.h>     
#include <sys/socket.h>
#include <netinet/in.h> //sockaddr_in
#include <stdio.h>
#include <string.h>

int main()
{
    int fd;
    int ret;
    char acbuf[20] = "";
    struct sockaddr_in serAddr = {0};

    //1.socket();
    fd = socket(PF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        perror("socket");
        return -1;
    }

    //2.连接connect() 服务器的地址
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(1234);
    serAddr.sin_addr.s_addr = inet_addr("192.168.159.6");
    ret = connect(fd,(struct sockaddr *)&serAddr,sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror("connect");
        return -1;
    }

    //3.通信
    while(1)
    {
        printf("send: ");
        fflush(stdout);
        scanf("%s",acbuf);
        if(strcmp(acbuf,"exit") == 0)
        {
            break;
        }
        write(fd,acbuf,strlen(acbuf));
    }

    //4.close()
    close(fd);
    return 0;
}

运行结果:
socket网络编程之TCP、UDP
做个改进,以上代码,只能一个客户端连接上。因为TCP是基于点对点的,一个accept()对应一个connnect()。要想连接多个客户端,就得使用fork(),一个进程用来专门阻塞等待客户端的连接,一个用来处理与已连接上客户端的通信。
代码如下:

server.c

int main()
    {
        int fd;
        int clientfd;
        int ret;
        pid_t pid;
        int addrLen = 0;
        char acbuf[20] = "";
        char client_addr[100] = "";
        struct sockaddr_in addr = {0};  //自己的地址
        struct sockaddr_in clientAddr = {0};    //连上的客户端的地址

        signal(SIGCHILD,SIG_IGN);

        //1.socket()
        fd = socket(PF_INET,SOCK_STREAM,0);
        if(fd == -1)
        {
            perror("socket");
            return -1;
        }

        //会出现没有活动的套接字仍然存在,会禁止绑定端口,出现错误:address already in use .
        //由TCP套接字TIME_WAIT引起,bind 返回 EADDRINUSE,该状态会保留2-4分钟
        //if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
            //{
        //      perror("setsockopet error\n");
        //      return -1;
            //}

        //2.bind()
        addr.sin_family = AF_INET;
        addr.sin_port = htons(1234);
        addr.sin_addr.s_addr = inet_addr("192.168.159.6");
        ret = bind(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in));
        if(ret == -1)
        {
            perror("bind");
            return -1;
        }

        //3.listen()
        ret = listen(fd,10);
        if(ret == -1)
        {
            perror("listen");
            return -1;
        }

        while(1)        
        {
            //4.阻塞等待 accept()
            clientfd = accept(fd,NULL,NULL);
            if(clientfd == -1)
            {
                perror("accept");
                return -1;
            }

            pid = fork();   //父进程负责继续监听等待,子进程父子与已连接客户端通信

            if(pid == -1)
            {
                perror("fork");
                return -1;
            }
            if(pid == 0)    //子进程
            {
                //获取客户端地址
                addrLen = sizeof(struct sockaddr_in);
                ret = getpeername(clientfd, (struct sockaddr *)&clientAddr, &addrLen);
                if(ret == -1)
                {
                    perror("getpeername");
                    return -1;
                }
                sprintf(client_addr,"ip: %s , port: %d\n",\
                    inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
                printf("client longin.\n%s\n",client_addr);

                //5.通信
                while(1)
                {
                    memset(acbuf,0,20);
                    if (read(clientfd,acbuf,20) == 0)   //客户端退出
                    {
                        //结束相应的server进程
                        close(clientfd);
                        exit(0);    //僵尸进程
                    }
                    printf("from %sreceive : %s\n\n",client_addr,acbuf);
                }
            }
            else    //父进程
            {
                //返回while,继续等待
            }

        }

        //6.close()
        close(fd);
        return 0;
    }

这里一定要注意,每结束一个客户端,一定要关掉相应的文件描述符,并且结束掉子进程(僵尸进程),不然,随着客户端的增加,进程数会越来越大
client.c

int main()
{
    int fd;
    int ret;
    int addrLen;
    char acbuf[20] = "";
    struct sockaddr_in serAddr = {0};
    struct sockaddr_in myAddr = {0};

    //1.socket();
    fd = socket(PF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        perror("socket");
        return -1;
    }

    //2.连接connect() 服务器的地址
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(1234);
    serAddr.sin_addr.s_addr = inet_addr("192.168.159.6");
    ret = connect(fd,(struct sockaddr *)&serAddr,sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror("connect");
        return -1;
    }

    //获取自己的地址
    addrLen = sizeof(struct sockaddr_in);
    ret = getsockname(fd,(struct sockaddr *)&myAddr,&addrLen);
    if(ret == -1)
    {
        perror("getsockname");
        return -1;
    }
    printf("client---ip: %s , port: %d\n",\
                inet_ntoa(myAddr.sin_addr),ntohs(myAddr.sin_port));
    //3.通信
    while(1)
    {
        printf("send: ");
        fflush(stdout);
        scanf("%s",acbuf);
        if(strcmp(acbuf,"exit") == 0)
        {
            break;
        }
        write(fd,acbuf,strlen(acbuf));
    }

    //4.close()
    close(fd);
    return 0;
}

运行结果:
socket网络编程之TCP、UDP

练习2-UDP

使用UDP连接,完成上述内容。但是发现,使用UDP,因为是面向无连接的,所以在没有收到或者发送包之前,是无法得知源IP地址的。
那UDP如何知道客户端的IP地址和端口呢?
1、由客户端显示地高速服务器IP地址和端口,发消息。
2、隐式的。服务器从收到的包头中得到源IP和端口号。
server.c

int main()
{
    int sockfd;
    int ret;
    char acbuf[20] = "";
    char client_addr[100] = "";
    struct sockaddr_in addr = {0};
    struct sockaddr_in clientAddr = {0};
    int addrLen = sizeof(struct sockaddr_in);
    int reuse = 0;

    //1.socket()
    sockfd = socket(PF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }

    //2.bind()
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1235);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    ret = bind(sockfd,(struct sockaddr *)&addr,addrLen);
    if(ret == -1)
    {
        perror("bind");
        return -1;
    }

    //3.通信
    while(1)
    {
        memset(acbuf,0,20);
        if(recvfrom(sockfd, acbuf, 100,0,(struct sockaddr *)&clientAddr,&addrLen) == -1)
        { 
            perror("recvfrom"); 
            return -1;
         } 
         //收到客户端的数据包之后,就可以知道客户端地址
        sprintf(client_addr," ip: %s , port: %d\n",\
                inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
         printf("receive from %s: %s\n",client_addr,acbuf);

    }

    //4.close
    close(sockfd);

    return 0;
}

client.c

int main()
{
    int sockfd;
    int ret;
    int addrLen = sizeof(struct sockaddr_in);
    char acbuf[20] = "";
    struct sockaddr_in serAddr = {0};
    struct sockaddr_in myAddr = {0};

    //1.socket()
    sockfd = socket(PF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(1235);
    serAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //2.通信
    while(1)
    {
        printf("send: ");
        fflush(stdout);
        scanf("%s",acbuf);
        if(strcmp(acbuf,"exit") == 0)
        {
            break;
        }
        sendto(sockfd, acbuf, 20,0,(struct sockaddr *)&serAddr,addrLen);

        //获取自己的地址
        ret = getsockname(sockfd,(struct sockaddr *)&myAddr,&addrLen);
        if(ret == -1)
        {
            perror("getsockname");
            return -1;
        }
        printf("client---ip: %s , port: %d\n\n",\
                    inet_ntoa(myAddr.sin_addr),ntohs(myAddr.sin_port));

    }

    //3.close
    close(sockfd);

    return 0;
}

运行结果:
socket网络编程之TCP、UDP
会发现,此时是可以直接运行多个客户端的,因为,UDP是面向无连接的,可以是一对多,多对一,多对多的,只要客户端知道服务器地址,就可以连上。
<br>
Ps:本人理解有限,还未学习完,有错请指出。

推荐阅读:
  1. TCP与UDP协议
  2. UDP-TCP

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

socket tcp udp

上一篇:Android之按钮

下一篇:关于Xcode上的Other linker flags

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》