day34——TCP和UDP的基础通信

一、网络通信之套接字

1.1 套接字通信原理

1.2 socket函数介绍

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
       功能:为通信创建一个端点,并返回该端点的文件描述符
       参数1:通信域
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   本地通信,同一主机之间进程通信     详情请看man 7 unix
       AF_INET             IPv4 提供的网络通信               详情请看man 7 ip
       AF_INET6            IPv6 提供的网络通信               详情请看man 7 ipv6
       参数2:指定通信语义,可以由多个宏值使用位或连接
       SOCK_STREAM:表示提供TCP协议的传输方式
       SOCK_DGRAM:表示提供UDP协议的传输方式
       SOCK_NONBLOCK:套接字设置非阻塞属性
       参数3:如果参数2中仅仅指定一个协议,那么参数3可以填0,如果指定多个,则参数3需要指定特定的协议
           TCP协议名称:IPPROTO_TCP
           UDP协议名称:IPPROTO_UDP
       返回值:成功返回创建的套接字文件描述符,失败返回     -1并置位错误码                 

二、TCP实现网络通信

2.1 TCP网络通信原理图

2.2 TCP相关函数介绍

1> bind绑定

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
       功能:位套接字分配名称
       参数1:通过socket函数创建出来的套接字文件描述符
       参数2:通用地址信息结构体,需要根据具体使用的地址族而定, struct sockaddr仅仅只是为了类型的强制转换,防止出现警告
           跨主机间通信:man 7 ip
            struct sockaddr_in {
               sa_family_t    sin_family; /* 表示通信域 */
               in_port_t      sin_port;   /* 端口号的网络字节序 */
               struct in_addr sin_addr;   /* ip地址 */
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* IP地址的网络字节序 */
           };
           同一主机间通信:man 7 uninx
          struct sockaddr_un {
               sa_family_t sun_family;               /* 表示通信域:AF_UNIX */
               char        sun_path[108];            /* 套接字文件的地址 */
           };

        参数3:参数2的大小
        返回值:成功返回0,失败返回-1并置位错误码
 注意关于bind的两个错误:
 1、 Cannot assign requested address:表示IP地址填写错误,检查IP是否有问题
 2、Address already in use:表示地址信息正在占用,可以调用函数快速重用,也可以等一会

2> listen监听

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
       功能:将套接字设置成被动监听状态,已接受客户端的连接请求
       参数1:套接字文件描述符
       参数2:容纳连接的队列的最大长度,一般填128
       返回值:成功返回0,失败返回-1并置为错误码
       

3> accept接收连接请求

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
       功能:用于阻塞接收客户端连接请求
       参数1:服务器套接字文件描述符
       参数2:用于接收对端地址信息结构体的指针
       参数3:接收对端地址信息的长度
       返回值:成功返回一个新的用于通信的套接字文件描述符,失败返回-1并置位错误码

4> recv接收数据

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

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       功能:从套接字中读取数据到buf中
       参数1:用于通信的套接字文件描述符
       参数2:接收数据后的容器地址
       参数3:接收的数据的大小
       参数4:是否阻塞接收
              0:表示阻塞接收消息
              MSG_DONTWAIT:表示非阻塞接收数据
        返回值:
            >0:表示成功读取的字符个数
            =0:表示通信对端已经下线
            =-1:表示出错,置位错误码                    

5> send发送数据

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

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
       功能:向通信套接字文件描述符中写入数据
       参数1:通信的套接字文件描述符
       参数2:要发送数据的起始地址
       参数3:要发送数据的大小
       参数4:是否阻塞接收
              0:表示阻塞接收消息
              MSG_DONTWAIT:表示非阻塞接收数据
       返回值:成功返回发送字符的个数,失败返回-1并置位错误码       

6> connect连接函数

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
       功能:将套接字文件描述符连接到addr指向的地址空间中
       参数1:客户端套接字文件描述符
       参数2:对端地址信息结构体
       参数3:参数2的大小
       返回值:成功返回0,失败返回-1并置位错误码

2.3 TCP服务器端代码实现

#include<myhead.h>
#define SER_PORT 6666          //服务器端口号
#define SER_IP "192.168.2.191"    //服务器ip地址


int main(int argc, const char *argv[])
{
    //1、创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    //参数1:表示ipv4的网络通信
    //参数2:表示使用的是TCP通信方式
    //参数3:表示默认使用一个协议
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success, sfd = %d\n", sfd);        //3

    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    //3、将套接字设置成被动监听状态
    if(listen(sfd, 128)==-1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");

    //4、阻塞等待客户端的连接请求
    //4.1 定义变量用于接收客户端的信息
    struct sockaddr_in cin;          //用于接收地址信息
    socklen_t addrlen = sizeof(cin);  //用于接收长度
    
    //4.2 接收连接
    int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen) ;
    if(newfd == -1)
    {
        perror("accept error");
        return -1;
    }
    printf("[%s:%d]: 已成功连接一个客户端\n", \
            inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

    //5、数据收发
    char buf[128] = "";

    while(1)
    {
        //清空容器
        bzero(buf, sizeof(buf));

        //从客户端套接字中读取数据
        int res = recv(newfd, buf, sizeof(buf), 0);
        if(res == -1)
        {
            perror("read error");
            return -1;
        }else if(res == 0)
        {
            printf("客户端已经下线\n");
            close(newfd);             //关闭客户端套接字
            break;
        }

        //正常收到客户端发来的消息
        printf("[%s:%d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);

        //对收到的消息进行一顿操作
        //此处省略一万行代码
        strcat(buf, "*_*");

        //将消息回复给客户端
        if(send(newfd, buf, strlen(buf), 0) == -1)
        {
            perror("发送error");
            return -1;
        }
        printf("发送成功\n");
    }

    //6、关闭监听
    close(sfd);

    return 0;
}

2.4 TCP服务器通信模型

1、sfd = socket();                //创建一个用于连接的套接字文件描述符
2、bind();                        //为服务器套接字绑定ip地址和端口号,为了让客户端额能够找到服务器
3、listen();                       //将服务器套接字设置成被动监听状态,用于接收客户端的连接请求
4、newfd = accept();              //阻塞等待客户端的连接请求,如果有客户端发来连接请求,创建一个新的用于通信的套接字文件描述符
5、while(1)
{
    send\recv\read\write;           //数据收发工作
}
6、close();                              //关闭套接字、关闭监听

2.5 TCP客户端实现

#include<myhead.h>

#define SER_PORT 6666             //与服务器保持一致
#define SER_IP  "192.168.2.191"    //服务器ip地址
#define CLI_PORT 8888               //客户端端口号
#define CLI_IP  "192.168.2.191"     //客户端ip地址


int main(int argc, const char *argv[])
{
    //1、创建用于通信的套接字文件描述符
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("cfd = %d\n", cfd);             //3
    
    //2、绑定IP地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

    //2.2 绑定工作
    if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    
    //3、连接到服务器
    //3.1 填充服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;          //通信域
    sin.sin_port = htons(SER_PORT);      //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

    //3.2 连接服务器
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");
    
    //4、数据收发
    char buf[128] = "";
    while(1)
    {
        printf("请输入>>>");
        fgets(buf, sizeof(buf), stdin);         //从终端获取一个字符串
        buf[strlen(buf)-1] = 0;

        //将数据发送给服务器
        send(cfd, buf, strlen(buf), 0);
        printf("发送成功\n");

        //接受服务器发来的数据
        //清空容器
        bzero(buf, sizeof(buf));
        recv(cfd, buf, sizeof(buf), 0);
        printf("收到服务器消息为:%s\n", buf);
    }
    
    //5、关闭套接字
    close(cfd);

    return 0;
}

三、UDP实现网络通信

3.1 UDP网络通信模型

3.2 UDP相关函数

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
       //功能:从套接字文件描述符中读取数据,并将对端地址信息结构体接收
       参数1:套接字文件描述符
       参数2:要接收数据的起始地址
       参数3:要接收的数据大小
       参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
       参数5:接收对端地址信息结构体
       参数6:参数5的大小
       返回值:成功返回读取的字节的大小,失败返回-1并置位错误码
       
        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
      //功能:向套接字文件描述符中读取数据,写给指定的对端接收
       参数1:套接字文件描述符
       参数2:要发送数据的起始地址
       参数3:要发送的数据大小
       参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
       参数5:接收对端地址信息结构体
       参数6:参数5的大小
       返回值:成功返回发送的字节的大小,失败返回-1并置位错误码
                                             

3.2 UDP服务器端实现

#include<myhead.h>

#define SER_PORT 9999          //服务器端口号
#define SER_IP "192.168.2.191"   //服务器ip地址

int main(int argc, const char *argv[])
{
    //1、创建用于通信的服务器套接字文件描述符
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", sfd);      //3


    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、数据收发
    char buf[128] = "";
    struct sockaddr_in cin;            //接受对端地址信息
    socklen_t addrlen = sizeof(cin);   //接受地址长度

    while(1)
    {
        //清空容器
        bzero(buf, sizeof(buf));

        //从套接字中读取数据
        recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
        printf("收到消息为:%s\n", buf);

        //将收到的消息进行处理
        //此处省略一万行
        strcat(buf, "*_*");

        if(sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cin, sizeof(cin)) == -1)
        {
            perror("发送error");
            return -1;
        }
        printf("发送成功\n");
    }
    

    //4、关闭文件描述符
    close(sfd);

    return 0;
}

3.3 UDP客户端实现

#include<myhead.h>

#define SER_PORT 9999          //服务器端口号
#define SER_IP "192.168.2.191"   //服务器ip地址
#define CLI_PORT 5555             //客户端端口号
#define CLI_IP  "192.168.2.191"    //客户端ip地址

int main(int argc, const char *argv[])
{
    //1、创建用于通信的服务器套接字文件描述符
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", cfd);      //3


    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

    //2.2 绑定工作
    if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、数据收发
    char buf[128] = "";
    //填充服务器的地址信息结构体
    struct sockaddr_in sin;            //接受对端地址信息
    sin.sin_family = AF_INET;      //服务器的通信域
    sin.sin_port = htons(SER_PORT);    //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

    while(1)
    {
        //从终端获取数据
        printf("请输入>>>>");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = 0;

        //将数据发送给服务器
        sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin));

        printf("发送成功\n");

        //清空容器
        bzero(buf, sizeof(buf));
        //接受服务器发来的消息
        recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
        printf("收到服务器消息为:%s\n", buf);

    }
    

    //4、关闭文件描述符
    close(cfd);

    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCPUDP是互联网传输层的两种不同的通信协议。它们使用不同的端口号来进行通信TCPUDP的端口号并不一样。TCP端口号被用于建立可靠的连接,而UDP端口号用于无连接的通信。具体的端口号可以根据具体的协议和服务来确定。可以查阅端口列表链接来获取TCPUDP协议中常用的端口号。 举个例子,22号端口用于SSH服务,它用于建立通过网络登录远程计算机的安全连接。 当进行TCP数据包分析时,需要注意不要混淆客户端端口和服务器端口。客户端端口是递增的,通常从1024开始,到4096时再次循环。如果你要调查的端口号在这个范围的较低部分,那么它可能是一个客户端端口。有状态的防火墙可以识别服务器端口,但数据包嗅探器和无状态的防火墙则不行。要确定一个TCP数据包的具体协议和服务,需要检查初始的TCP握手过程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [tcp/udp 常用端口列表](https://blog.csdn.net/whatday/article/details/106235441)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [常用网络端口号](https://blog.csdn.net/u011308691/article/details/16357477)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值