TCP和UDP与IO模型

TCP和UDP与IO模型

关键词: tcp udp

主要内容:

  • tcp
  • udp
  • tcp 多路复用
  • udp 非阻塞

文档参考:

最新地址: https://taotaodiy-linux.readthedocs.io/en/latest/linux/base/tcpudp.html

tcp 实现代码 (C语言)

TCP通信模型

一对一通信代码

客户端

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void)
{
    int cli_socket;
    int ret;
    struct sockaddr_in serv_addr;
    char buf[128];
    /*
        #include <sys/types.h>           
        #include <sys/socket.h>
            int socket(int domain, int type, int protocol);

            domain:
                AF_UNIX     进程间通信 socket
                    AF_INET     ipv4  通信
                AF_PACKET  原始套接字
            type:
                SOCK_STREAM  流式套接字   tcp通信
                SOCK_DGRAM  数据包套接字  udp通信
                SOCK_RAW    原始套接字
            protocol
                0
    */
    cli_socket = socket(AF_INET,SOCK_STREAM,0);
    if(cli_socket < 0){
        perror("socket err");
        return -34;
    }
    /*向服务器 发起连接请求
        connect
    #include <sys/types.h>           
    #include <sys/socket.h>
    int connect(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
            struct sockaddr_in {
            sa_family_t    sin_family;    AF_INET  
            in_port_t      sin_port;    port 16bit   网络字节序  
            struct in_addr sin_addr;     internet address  ip地址
        };
            Internet address. 
        struct in_addr {
            uint32_t       s_addr;      address in network byte order  
        };
    */
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8888);
    serv_addr.sin_addr.s_addr = inet_addr("132.232.72.168");
    ret = connect(cli_socket, (struct sockaddr *)&serv_addr,sizeof(serv_addr));
    if(ret < 0 ){
        perror("connect err");
        return -34;
    }
    while(1){
        memset(buf,0,sizeof(buf));
        //strcpy(buf,"hello ,this is cli  msg!");
        scanf("%s", buf);
        ret = send(cli_socket,buf,sizeof(buf),0);
        if(ret <0){
            perror("cli send err");
            return -31;
        }
        memset(buf,0,sizeof(buf));
        ret = recv(cli_socket,buf,sizeof(buf),0);
        if(ret <0){
            perror("recv err");
            return -322;
        }
        if(ret == 0){
            printf("cli  recv get 0\n");
            return -345;
        }
        printf("cli get serv msg:%s\n",buf);
        sleep(1);
    }
    close(cli_socket);
}

服务器端

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void)
{
    int listen_socket;
    int cli_socket;
    int ret;
    struct sockaddr_in serv_addr;
    char buf[128];
    /*
        #include <sys/types.h>           
        #include <sys/socket.h>
            int socket(int domain, int type, int protocol);
            domain:
                AF_UNIX     进程间通信 socket
                    AF_INET     ipv4  通信
                AF_PACKET  原始套接字
            type:
                SOCK_STREAM  流式套接字   tcp通信
                SOCK_DGRAM  数据包套接字  udp通信
                SOCK_RAW    原始套接字
            protocol
                0
    */
    listen_socket = socket(AF_INET,SOCK_STREAM,0);
    if(listen_socket < 0){
        perror("socket err");
        return -34;
    }
    /*
    给socket 指定 ip和端口号

        int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

                struct sockaddr_in {
                    sa_family_t   sin_family;    AF_INET  
                    in_port_t     sin_port;    port 16bit   网络字节序  
                    struct in_addr sin_addr;     internet address  ip地址
                };

                Internet address. 
                struct in_addr {
                    uint32_t      s_addr;      address in network byte order  
                };
    */
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8888);
    //serv_addr.sin_addr.s_addr = inet_addr("132.232.72.168");
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(listen_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    if(ret <0){
        perror("bind err");
        return -34;
    }
    /*
        int listen(int sockfd, int backlog);
        backlog:
            同一个瞬间,可以同时处理多少个请求 
            2*backlog+1
    */
    ret = listen(listen_socket,5);
    if(ret <0){
        perror("listen err");
        return -34;
    }
    /*当有 客户端连接上来的时候,accept 返回
        返回一个 新的socket,该新的socket 用于和客户端通信,表示一个新的连接

        int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        addr:  对方/客户端的 ip地址和端口

    */
    cli_socket = accept(listen_socket,NULL,NULL);
    if(cli_socket <0){
        perror("accept err");
        return -23;
    }
    while(1){
        /*
        服务器, 先收后发
        ssize_t recv(int sockfd, void *buf, size_t len, int flags);
        sockfd:通信socket
        buf:接收的数据存放的位置
        len: 你要求收取多少数据
        flags:  0

        返回值,
            >0 实际收取的大小
            =0 
            <0  表示收取失败
        */
        memset(buf,0,sizeof(buf));
        ret = recv(cli_socket,buf,sizeof(buf),0);
        if(ret<0){
            printf("recv err %d\n",errno);
            return -25;
        }
        if(ret == 0){
            printf("recv ==0 \n");
            return -34;
        }

        printf("serv get data:%s\n",buf);
        /*解析数据*/
        memset(buf,0,sizeof(buf));
        strcpy(buf,"Ur MSG got, this is server!");

        ret = send(cli_socket,buf,sizeof(buf),0);
        if(ret<0){
            printf("send err %d\n",errno);
            return -25;
        }

    }
    close(cli_socket);
}

comm.h 放传输数据用的结构体(也就是所谓的协议)

#ifndef  COMM_HHH_H
#define COMM_HHH_H

struct msg {
    char type;
    short sval;
    int  ival;
    char des[128];
};

#endif

makefile

all:
    gcc client.c  -o client
    gcc server.c -o server

一对多通信代码

一对多通信 主要是服务器代码不同,这里用多线程

服务器端

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include "comm.h"
struct  pri {
    pthread_t pid;
    int socket;
    int xpid;
    //...................
};
void *serv_for_cli (void *args)
{
    int ret;
    struct  pri *pri = args;
    int cli_socket = pri->socket;
    struct msg msg;
    while(1){
        memset(&msg,0,sizeof(msg));
        ret = recv(cli_socket,&msg,sizeof(msg),0);
        if(ret<0){
            printf("recv err %d\n",errno);
            goto end;
        }
        if(ret == 0){
            printf("recv ==0  peer client shutdown!!!!\n");
            goto end;
        }

        printf("serv get type%d sval%d ival%d des:%s\n",
            msg.type,ntohs(msg.sval), ntohl(msg.ival),msg.des);
        /*解析数据*/
        msg.type++;

        msg.sval = ntohs(msg.sval);
        msg.sval++;
        msg.sval = htons(msg.sval);
        strcpy(msg.des,"Ur MSG got, this is server!");

        ret = send(cli_socket,&msg,sizeof(msg),0);
        if(ret<0){
            printf("send err %d\n",errno);
            goto end;
        }
    }

end:
    free(pri);
    close(cli_socket);
    return NULL;
}
int main(void)
{
    int listen_socket;
    int cli_socket;
    int ret;
    struct pri *pri;

    struct sockaddr_in serv_addr;
    struct sockaddr_in cli_addr;
    socklen_t len;
    listen_socket = socket(AF_INET,SOCK_STREAM,0);
    if(listen_socket < 0){
        perror("socket err");
        return -34;
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(3100);
    //serv_addr.sin_addr.s_addr = inet_addr("192.168.2.92");    //指定服务器ip
    serv_addr.sin_addr.s_addr = INADDR_ANY;         //给一个0,bind发现 ip为0,则自动获取主机ip地址
    ret = bind(listen_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    if(ret <0){
        perror("bind err");
        return -34;
    }
    ret = listen(listen_socket,5);
    if(ret <0){
        perror("listen err");
        return -34;
    }
loop:
    len = sizeof(cli_addr);
    memset(&cli_addr,0,sizeof(cli_addr));
    cli_socket = accept(listen_socket,(struct sockaddr *)&cli_addr,&len);
    if(cli_socket <0){
        perror("accept err");
        return -23;
    }
    printf("serv get client  conncet: cli ip %s  port %d\n",
            inet_ntoa(cli_addr.sin_addr),  ntohs(cli_addr.sin_port)  );
    pri = malloc(sizeof(*pri));
    pri->socket = cli_socket;
    pthread_create(&pri->pid, NULL,serv_for_cli,pri);
    goto loop;
    close(listen_socket);
}

udp 实现代码 (C语言)

DUP通信模型{.align-center}

客户端

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "comm.h"
int main(void)
{
    int ret;
    int cli_socket;
    socklen_t addrlen;
    struct sockaddr_in serv_addr;
    struct  msg msg;
    cli_socket = socket(AF_INET,SOCK_DGRAM, 0);
    if(cli_socket < 0 ){
        perror("udp serv socket err");
        return -34;
    }
    memset(&msg,0,sizeof(msg));
    msg.type = 0;
    msg.sval = 12;
    msg.ival = 124;
    strcpy(msg.des,"hello comes from cli");
    while(1){
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(3100);
        serv_addr.sin_addr.s_addr = inet_addr("192.168.2.92");
        ret = sendto(cli_socket,&msg,sizeof(msg),0,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
        if(ret < 0){
            printf("sendto err%d\n",ret);
            return -34;
        }
        memset(&msg,0,sizeof(msg));
        addrlen = sizeof(serv_addr);
        ret = recvfrom(cli_socket, &msg, sizeof(msg), 0,(struct sockaddr *)&serv_addr, &addrlen);
        if(ret <=0){
            printf("recvfrom err%d\n",ret);
            return -34;
        }
        printf("cli get msg: type%d sval%d ival%d des:%s\n",
            msg.type,msg.sval,msg.ival,msg.des);
        sleep(1);   
    }
}

服务器端

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "comm.h"
int main(void)
{
    int ret;
    int serv_socket;
    struct sockaddr_in serv_addr;
    struct sockaddr cli_addr;
    socklen_t  addrlen;
    struct  msg msg;
/*
        int socket(int domain, int type, int protocol);
        domain:
            AF_UNIX     进程间通信 socket
                AF_INET     ipv4  通信
            AF_PACKET  原始套接字
        type:
            SOCK_STREAM  流式套接字   tcp通信
            SOCK_DGRAM  数据包套接字  udp通信
            SOCK_RAW    原始套接字
        protocol
            0
*/
    serv_socket = socket(AF_INET,SOCK_DGRAM, 0);
    if(serv_socket < 0 ){
        perror("udp serv socket err");
        return -34;
    }
/*
    给socket 指定 ip和端口号
    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
            struct sockaddr_in {
                sa_family_t   sin_family;    AF_INET  
                in_port_t     sin_port;    port 16bit   网络字节序   
                struct in_addr sin_addr;    internet address  ip地址
            };
            Internet address. 
            struct in_addr {
                uint32_t      s_addr;      address in network byte order  
            };
*/
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(3100);
    serv_addr.sin_addr.s_addr = INADDR_ANY;         //给一个0,bind发现 ip为0,则自动获取主机ip地址
    ret = bind(serv_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    if(ret <0){
        perror("bind err");
        return -34;
    }
    while(1){
    /*
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);
    buf,len 用于存放接收到的数据
    src_addr,存放对方/客户端 的地址
    addrlen, 双向参数  你要告诉recvfrom 你的地址长度有多大,  该函数返回,告诉你真实的长度是多少
    返回值
            >=0 正确接收到的数据
            <0   出错
    */
        memset(&msg,0,sizeof(msg));
        addrlen = sizeof(cli_addr);
        ret = recvfrom(serv_socket, &msg, sizeof(msg), 0,&cli_addr, &addrlen);
        if(ret <=0){
            printf("recvfrom err%d\n",ret);
            return -34;
        }
        printf("serv get msg: type%d sval%d ival%d des:%s\n",
            msg.type,msg.sval,msg.ival,msg.des);
        msg.type++;
        msg.sval++;
        /*
        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                                const struct sockaddr *dest_addr, socklen_t addrlen);
        */
        ret = sendto(serv_socket,&msg,sizeof(msg),0,&cli_addr,sizeof(cli_addr));
        if(ret < 0){
            printf("sendto err%d\n",ret);
            return -34;
        }
    }
}

tcp的多路复用(C语言)

普通tcp和udp使用就是同步阻塞,recv()、recvfrom()等函数就是阻塞死等;
这里主要是
套接字的非阻塞和tcp的多路复用,udp天生具有并发,就不画蛇添足了。

服务器端

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>  
#include <arpa/inet.h>
#include <string.h>
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <unistd.h>
#include "comm.h"
/*
void FD_CLR(int fd, fd_set *set);       将fd从set清除
int  FD_ISSET(int fd, fd_set *set);     判断fd是否存在集合中
void FD_SET(int fd, fd_set *set);       将fd加入到set
void FD_ZERO(fd_set *set);              清空set
阻塞式的监控
int select(int nfds, fd_set *readfds, fd_set *writefds,
                 fd_set *exceptfds, struct timeval *timeout);
nfds:
    集合中所有fd的  最大值+1
readfds: 可读事件,表示 当前可读fd的 一个集合
    如果你想监控 某些socket/fd 的可读事件,那么 就将他们加入到该集合
    双向参数: 你在调用之前,将需要监控的所有fd都加进去
        等select返回的时候,里面只保存了 发生事件的fd
            所以, 你需要备份
writefds: 可写...................
    如果你想监控 .....................可写.................
        双向参数: 你在调用之前,将需要监控的所有fd都加进去
        等select返回的时候,里面只保存了 发生事件的fd
            所以, 你需要备份
exceptfds:异常事件,表示 fd发生异常的集合
    如果你想监控 异常事件 ............................
        双向参数: 你在调用之前,将需要监控的所有fd都加进去
            等select返回的时候,里面只保存了 发生事件的fd
                所以, 你需要备份
timeout:    指定等待的最大时间,如果在改时间内 没有 任何fd发生事件,
    返回
    struct timeval {
            long    tv_sec; 
            long    tv_usec; 
        };
    ==NULL, 永久监控
    ==非阻塞监控timeout->tv_sec=timeout->tv_usec = 0;
返回值:
    -1:  错误
    正值: 发生事件的fd的个数
    0:      超时了
*/
struct sockaddr_in serv_local;
struct sockaddr_in cli_addr;
size_t addr_sz;
int socket_serv;
int socket_client;
fd_set readfds;     //读集合
fd_set readfds_backup;      //读集合
int fd_max;
struct timeval  timeout;
int fd_client[128];
int fd_client_cnt = 0;

int serv_for_client(int socket_client)
{
    int ret;
    struct msg_type msg;
    memset(&msg,0,sizeof(msg));
    ret = recv(socket_client,&msg,sizeof(msg),0);
    if(ret <0){
        perror("recv err");
        close(socket_client);
        return -35;
    }else if(ret == 0){
        printf("server detect client over\n");
        close(socket_client);
        return -25;
    }
    printf("serv get msg:type%d tmp%d himmdy%d des: %s\n",
    msg.msg_type,ntohs(msg.tmp),ntohl(msg.himmdty),msg.des);
    msg.tmp = ntohs(msg.tmp);
    msg.himmdty = ntohl(msg.himmdty);
    msg.tmp++;      //主机字节序
    msg.himmdty += 3;
    strcpy(msg.des,"hello from serv");
    msg.tmp = htons(msg.tmp);
    msg.himmdty = htonl(msg.himmdty);
    ret = send(socket_client, &msg, sizeof(msg),0);
    if(ret <0){
        perror("send err");
        close(socket_client);
        return -36;
    } 
    return 0;
}
int main(int argc,char **argv)
{
    int ret;
    int idx;
    socket_serv = socket(AF_INET,SOCK_STREAM, 0);
    if(socket_serv <0 ){
        perror("server socket err");
        return -12;
    }
    serv_local.sin_family = AF_INET;
    serv_local.sin_port =  htons(5050);
    serv_local.sin_addr.s_addr = INADDR_ANY; /*INADDR_ANY = 0  代码自动绑定本机ip地址*/
    ret = bind(socket_serv,(struct sockaddr *)&serv_local,sizeof(serv_local));
    if(ret <0){
        perror("bind err");
        return -13;
    }
    ret = listen(socket_serv,5);
    if(ret <0){
        perror("listen err");
        return -14;
    }
    /*当前有一个socket,那么 加入到监控集合中去
    */
    FD_ZERO(&readfds);
    FD_SET(socket_serv,&readfds);
    fd_max = socket_serv;
loop:
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    readfds_backup = readfds;
    ret = select(fd_max+1,&readfds_backup,NULL,NULL,&timeout);
    if(ret <0){
        perror("select err");
        return -23;
    }else if(ret == 0){
        printf("select timeout, next time\n");
        goto loop;
    }
    /*readfds_backup 里面保存了发生事件的fd,遍历*/
    if(FD_ISSET(socket_serv,&readfds_backup)){  //有新的客户端发起连接
        memset(&cli_addr,0,sizeof(cli_addr));
        addr_sz = sizeof(cli_addr);
        socket_client = accept(socket_serv,(struct sockaddr *)&cli_addr,&addr_sz);
        if(socket_client < 0){
            perror("serv accept err");
            return -34;
        }
        printf("serv get client ip %s port %d\n",
            inet_ntoa(cli_addr.sin_addr) ,ntohs(cli_addr.sin_port));
        /*将新的套接字 加入监控空集合*/
        FD_SET(socket_client,&readfds);
        fd_max = fd_max > socket_client ? fd_max : socket_client;
        fd_client[fd_client_cnt++] = socket_client;
    }
    for(idx=0;idx<fd_client_cnt;idx++){
        if(FD_ISSET(fd_client[idx],&readfds_backup)){
            ret = serv_for_client(fd_client[idx]);
            if(ret <0){
                fd_client[idx] = 0;  //清空
            }
        }
    }
    goto loop;      //继续监控
    close(socket_serv);
    return 0;
}

非阻塞UDP(C语言)

服务器端

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.h"

int main(void)
{
    int flags;
    int ret;
    int serv_socket;
    struct sockaddr_in serv_addr;
    struct sockaddr cli_addr;
    socklen_t  addrlen;
    struct  msg msg;

    serv_socket = socket(AF_INET,SOCK_DGRAM, 0);
    if(serv_socket < 0 ){
        perror("udp serv socket err");
        return -34;
    }
    flags = fcntl(serv_socket,F_GETFL,0);
    flags |= O_NONBLOCK;
    fcntl(serv_socket,F_SETFL,flags);

    /*如何让 socket/fd 编程非阻塞呢
        1.读取 socket里面的 flags
            flags = fcntl(socket,F_GETFL,0);
        2.将flags对应的bit置一
            flags |= O_NONBLOCK;
        3.将flags写入socket文件内部
            fcntl(socket,F_SETFL,flags);
    */
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(3100);
    serv_addr.sin_addr.s_addr = INADDR_ANY;         //给一个0,bind发现 ip为0,则自动获取主机ip地址
    ret = bind(serv_socket,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    if(ret <0){
        perror("bind err");
        return -34;
    }
    while(1){
        /*
        非阻塞 返回值的问题
        1.正确收到数据    ret>0
        2.没有数据      ret<0   errno==EAGAIN
        3.出错            ret<0
        */
loop:
        memset(&msg,0,sizeof(msg));
        addrlen = sizeof(cli_addr);
        ret = recvfrom(serv_socket, &msg, sizeof(msg), 0,&cli_addr, &addrlen);
        if(ret < 0){
            if(errno == EAGAIN){
                printf("recv current nodata,try next\n");
                void do_something_other(void);
                sleep(1);
                goto loop;
            }

            printf("recvfrom err%d\n",ret);
            return -34;
        }
        printf("serv get msg: type%d sval%d ival%d des:%s\n",
            msg.type,msg.sval,msg.ival,msg.des);
        msg.type++;
        msg.sval++;
        ret = sendto(serv_socket,&msg,sizeof(msg),0,&cli_addr,sizeof(cli_addr));
        if(ret < 0){
            printf("sendto err%d\n",ret);
            return -34;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

贪贪贪丶慎独

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

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

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

打赏作者

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

抵扣说明:

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

余额充值