TCP与UDP连接的客户端与服务端的创建

TCP 与 UDP连接

在这里插入图片描述

1.TCP基础模块代码
  1. socket 创建套接字
功能:在内核空间创建发送缓冲区和接收缓冲区,并在用户空间中返回文件描述符;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
参数:
    int domain:指定地址族(协议族)
        Name           Purpose(目的)                 Man page(man手册)
       AF_UNIX,AF_LOCAL  Local communication           unix(7)         man 7 unix
       
       AF_INET        IPv4 Internet protocols          ip(7)           man 7 ip
       AF_INET6       IPv6 Internet protocols          ipv6(7)
    int type:套接字类型      
        SOCK_STREAM:字节流式套接字,流式套接字 ---》默认使用TCP协议。
        SOCK_DGRAM:数据报式套接字,报式套接字  ---》默认使用UDP协议        
        SOCK_RAW:原始套接字,具体协议类型需要再第三个参数指定.
    int protocol:填0,代表默认协议。
        常用协议有:IPPROTO_TCP     IPPROTO_UDP     IPPROTO_IP
返回值:
    成功,返回文件描述符;
    失败,返回-1,更新errno;
  1. bind 绑定服务器的IP和端口
功能:将地址信息绑定到指定的套接字文件上;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
参数:
    int sockfd:指定要绑定到哪个套接字上;
    struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族来指定。
                            该指针指向的空间中需要手动填充上地址信息,给bind函数绑定使用。
        地址族:AF_INET ---> man 7 ip
           struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET;
               in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序:1024~49151
               struct in_addr sin_addr;   /* internet address */    IP地址的网络字节序。ifconfig查询
                                                                    服务运行在哪个主机就填哪个主机的IP地址。
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };
    socklen_t addrlen:真实的地址信息结构体的大小。sizeof(struct sockaddr_in)
返回值:
    成功,返回0;
    失败,返回-1,更新errno;
sockaddr_in结构体是为IPv4地址指定的专用结构,它用于在网络编程中指定套接字地址信息。这个结构体是sockaddr通用结构体的一个特化版本。

以下是sockaddr_in结构体的定义:


struct sockaddr_in {
    sa_family_t    sin_family; // 地址族 (AF_INET)
    in_port_t      sin_port;   // 端口号 (使用网络字节序)
    struct in_addr sin_addr;   // IPv4地址 (使用网络字节序)
    char           sin_zero[8]; // 填充,保持与struct sockaddr同样大小
};
详细解释
sin_family:地址族,必须设置为AF_INET表示IPv4地址。
sin_port:端口号,使用htons函数将主机字节序转换为网络字节序。
sin_addr:struct in_addr结构,表示IPv4地址,通常使用inet_addr函数或INADDR_ANY。
sin_zero:填充字段,使得sockaddr_in结构体大小与sockaddr一致。通常不使用,可以用memset初始化为0。
sockaddr 与 sockaddr_in
sockaddr是一个通用的地址结构体,它可以表示不同协议族(如IPv4和IPv6)的地址。为了兼容和通用,许多网络函数接受struct sockaddr *类型的参数,但具体操作时会使用特定协议族的结构体,如sockaddr_in或sockaddr_in6。

-----------------------------------
inet_addr函数用于将IPv4地址从点分十进制字符串(如"192.168.0.1")转换为网络字节序的二进制形式。这个二进制形式可以用于网络编程中的各种网络操作,例如设置套接字地址结构中的地址字段。
  1. listen 将套接字设置为被动监听状态
功能:将套接字设置为被动监听状态;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
参数:
    int sockfd:指定将哪个套接字设置为被动监听状态;
    int backlog:指定内核中队列的大小,不要填01,一般填128.
返回值:
    成功,返回0;
    失败,返回-1,更新errno;
  1. accept (阻塞函数)
功能:阻塞等待客户端连接成功。当客户端连接成功后,会从已完成连接的队列头中获取一个客户端信息,
     生成一个新的文件描述符,该文件描述符才是与客户端通信的文件描述符。
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
    int sockfd:被转换成监听状态的文件描述符,通过该文件描述符才能找到已完成连接的队列。
    struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族来指定。
                            存储获取到的客户端的地址信息,若不想获取填NULL;
        地址族:AF_INET ---> man 7 ip
           struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET;
               in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序:1024~49151
               struct in_addr sin_addr;   /* internet address */    IP地址的网络字节序。ifconfig查询
                                                                    服务运行在哪个主机就填哪个主机的IP地址。
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };
    socklen_t *addrlen:该指针指向的空间中需要填充上真实的地址信息结构体大小。
                        若有addr参数填NULL,没有才填NULL。
返回值:
    成功,返回新的文件描述符,该文件描述符才是用于通信的文件描述符;
    失败,返回-1,更新errno;
  1. recv
功能:接收。通过通信套接字,从接收缓冲区中读取数据。
原型:
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
    int sockfd:通信套接字文件描述符;
    void *buf:接收到的数据存储到该指针指向的空间中;
    size_t len:指定接收的字节数;
    int flags:
        0,阻塞方式接收,当接收缓冲区中没有数据,则recv函数阻塞。当flag==0,recv等价于read函数。
        MSG_DONTWAIT:非阻塞方式接收,当接收缓冲区中没有数据,则recv函数不阻塞,且函数运行失败,
                      errno==EAGAIN or EWOULDBLOCK;
返回值:
    >0, 成功,接收到的字节数;
    =-1,函数运行失败,更新errno;
    =0, 对端关闭。
  1. send
功能:向指定的套接字发送数据;
原型:
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
    int sockfd:通信套接字文件描述符;
    void *buf:指定要发送的数据首地址。
    size_t len:指定发送的字节数;
    int flags:
        0,阻塞方式发送,当发送缓冲区中满了,则send函数阻塞。当flag==0,send等价于write函数。
        MSG_DONTWAIT:非阻塞方式发送,当发送缓冲区满了,则send函数不阻塞,且函数运行失败,
                      errno==EAGAIN or EWOULDBLOCK;
返回值:
    >0, 成功,发送的字节数;
    =-1,函数运行失败,更新errno;
  1. connect
connect函数用于建立与远程主机的连接,适用于客户端程序。它的原型如下:

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

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数解释:
sockfd:要连接的套接字描述符,由socket函数创建并初始化。
addr:指向struct sockaddr类型的指针,包含要连接的远程主机的地址信息。
addrlen:addr结构的长度(以字节为单位)。
返回值:
成功时返回0。
失败时返回-1,并设置errno来指示错误的类型。

TCPSer

#include<stdio.h>                          // 引入标准输入输出库
#include<sys/types.h>                      // 引入系统类型定义库
#include<sys/socket.h>                     // 引入套接字库
#include<arpa/inet.h>                      // 引入arpa网络库
#include<string.h>                         // 引入字符串处理库
#include<netinet/in.h>                     // 引入互联网地址族库
#include<unistd.h>                         // 引入Unix标准函数库
#define PORT 8888                          // 定义端口号为8888-------》指定的随机端口号
#define IP "192.168.124.111"               // 定义IP地址为192.168.124.111------》运行主机的IP
#define ERR_MSG(msg)  {fprintf(stderr,"line:%d",__LINE__);perror(msg);} // 定义错误消息输出宏

int main(int argc, const char *argv[]) {
    int sfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字  SOCK_STREAM流式套接字
    if (sfd < 0) {
        ERR_MSG("socket");                    // 套接字创建失败,打印错误消息
        return -1;                            // 返回错误码
    }
    printf("流式套接字创建完毕 sfd=%d\n", sfd); // 套接字创建成功,打印套接字描述符

    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        ERR_MSG("setsockopt");               // 设置套接字选项失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("允许端口快速重用\n");            // 套接字选项设置成功,允许端口快速重用
    
	//填充服务器的地址信息结构体,给bind函数使用
	//真实的地址信息结构体根据地址族指定
	//填进去之后必须强制转换//(struct sockaddr*)&sin
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 设置地址族为AF_INET
    sin.sin_port = htons(PORT);              // 设置端口号
    sin.sin_addr.s_addr = inet_addr(IP);     // 设置IP地址
	
	//绑定服务器的地址信息,必须绑定,对于TCP而言
    if (bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
        ERR_MSG("bind");                     // 绑定失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("绑定成功\n");                    // 绑定成功,打印消息
	
	//将套接字设置为被动监听状态
    if (listen(sfd, 128) < 0) {
        ERR_MSG("listen");                   // 监听失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("监听成功\n");                    // 监听成功,打印消息
	
	//获取连接成功的客户端信息,生成一个新的套接字文件描述符
    struct sockaddr_in cin;// 存储客户端的信息
    socklen_t addrlen = sizeof(cin);
    int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen); // 接受客户端连接
    if (newfd < 0) {
        ERR_MSG("accept");                   // 接受连接失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("[%s:%d] newfd=%d 客户端链接成功\n",\
            inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd); // 打印客户端连接成功消息

    char buf[128] = "";
    ssize_t res;
    while (1) {
        bzero(buf, sizeof(buf));             // 清空缓冲区
        res = recv(newfd, buf, sizeof(buf), 0); // 接收数据
        if (res < 0) {
            ERR_MSG("recv");                 // 接收数据失败,打印错误消息
            return -1;                       // 返回错误码
        } else if (0 == res) {
            printf("[%s:%d] newfd=%d 客户端下线\n",\
                    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd); // 客户端下线,打印消息
            break;                           // 跳出循环
        }
        printf("[%s:%d] newfd=%d : %s\n",\
                inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf); // 打印接收到的消息
		//发送
        strcat(buf, "*_*");                  // 追加字符到接收到的消息
        if (send(newfd, buf, sizeof(buf), 0) < 0) {
            ERR_MSG("send");                 // 发送数据失败,打印错误消息
            return -1;                       // 返回错误码
        }
        printf("发送成功\n");                // 发送成功,打印消息
    }
    close(newfd);                            // 关闭新连接的套接字
    if (close(sfd) < 0) {
        ERR_MSG("close");                    // 关闭套接字失败,打印错误消息
        return -1;                           // 返回错误码
    }

    return 0;                                // 返回成功码
}

TCPCLINET

#include<stdio.h>                          // 引入标准输入输出库
#include<sys/types.h>                      // 引入系统类型定义库
#include<sys/socket.h>                     // 引入套接字库
#include<arpa/inet.h>                      // 引入arpa网络库
#include<string.h>                         // 引入字符串处理库
#include<netinet/in.h>                     // 引入互联网地址族库
#include<unistd.h>                         // 引入Unix标准函数库

#define PORT 8888                          // 定义端口号为8888
#define IP "192.168.124.111"               // 定义IP地址为192.168.124.111
#define ERR_MSG(msg)  {fprintf(stderr,"line:%d",__LINE__);perror(msg);} // 定义错误消息输出宏

int main(int argc, const char *argv[]) {
    int cfd = socket(AF_INET, SOCK_STREAM, 0); // 创建客户端套接字
    if (cfd < 0) {
        ERR_MSG("socket");                    // 套接字创建失败,打印错误消息
        return -1;                            // 返回错误码
    }
    printf("流式套接字创建完毕 cfd=%d\n", cfd); // 套接字创建成功,打印套接字描述符

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 设置地址族为AF_INET
    sin.sin_port = htons(PORT);              // 设置端口号
    sin.sin_addr.s_addr = inet_addr(IP);     // 设置IP地址

    if (connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
        ERR_MSG("connect");                  // 连接服务器失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("连接服务器[%s:%d]成功\n", IP, PORT); // 连接服务器成功,打印消息

    char buf[128] = "";                      // 定义缓冲区
    ssize_t res;                             // 定义接收返回值
    while (1) {
        bzero(buf, sizeof(buf));             // 清空缓冲区
        printf("请输入>>>");                 // 提示用户输入
        fgets(buf, sizeof(buf), stdin);      // 从标准输入读取用户输入
        buf[strlen(buf) - 1] = '\0';         // 将换行符替换为字符串结束符

        if (send(cfd, buf, sizeof(buf), 0) < 0) {
            ERR_MSG("send");                 // 发送数据失败,打印错误消息
            return -1;                       // 返回错误码
        }
        printf("发送成功\n");                // 发送数据成功,打印消息

        bzero(buf, sizeof(buf));             // 清空缓冲区
        res = recv(cfd, buf, sizeof(buf), 0); // 接收服务器返回数据
        if (res < 0) {
            ERR_MSG("recv");                 // 接收数据失败,打印错误消息
            return -1;                       // 返回错误码
        } else if (0 == res) {
            printf("[%s:%d] cfd = %d 服务器下线\n", IP, PORT, cfd); // 服务器断开连接,打印消息
            break;                           // 跳出循环
        }
        printf("[%s:%d] newfd = %d : %s\n", IP, PORT, cfd, buf); // 打印接收到的消息
    }

    if (close(cfd) < 0) {
        ERR_MSG("close");                    // 关闭套接字失败,打印错误消息
        return -1;                           // 返回错误码
    }

    return 0;                                // 返回成功码
}

2.UDP基础模块代码

在这里插入图片描述

  1. socket
功能:在内核空间创建发送缓冲区和接收缓冲区,并在用户空间中返回文件描述符;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
参数:
    int domain:指定地址族(协议族)
        Name           Purpose(目的)                 Man page(man手册)
    AF_UNIX,AF_LOCAL  Local communication              unix(7)         man 7 unix
       
       AF_INET        IPv4 Internet protocols          ip(7)           man 7 ip
       AF_INET6       IPv6 Internet protocols          ipv6(7)
    int type:套接字类型      
        SOCK_STREAM:字节流式套接字,流式套接字 ---》默认使用TCP协议。
        SOCK_DGRAM:数据报式套接字,报式套接字  ---》默认使用UDP协议        
        SOCK_RAW:原始套接字,具体协议类型需要再第三个参数指定.
    int protocol:填0,代表默认协议。
        常用协议有:IPPROTO_TCP     IPPROTO_UDP     IPPROTO_IP
返回值:
    成功,返回文件描述符;
    失败,返回-1,更新errno;
  1. bind
功能:将地址信息绑定到套接字文件上;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
参数:
    int sockfd:指定将地址信息绑定到哪个套接字上。需要绑定到监听套接字上。
    struct sockaddr *addr:通用地址信息结构体,存储地址信息的。但是真正的地址信息结构体根据地址族指定。
        IPV4-->AF_INET-->man 7 ip 
           struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
               in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序,1024~49151
               struct in_addr sin_addr;   /* internet address */
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */ IP地址的网络字节序
           };
        IPV6-->AF_INET6-->man 7 ipv6
    socklen_t addrlen:真实的地址信息结构体的大小。sizeof(struct sockaddr_in);
返回值:
     成功,返回0;
     失败,返回-1,更新errno;       
  1. recvfrom
功能:接收数据,同时可以获取到该数据包从哪里来,即可以获取到发送方的地址信息。
原型:
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
参数:
    int sockfd:通信套接字文件描述符;
    void *buf:接收到的数据存储到该指针指向的空间中;
    size_t len:指定接收的字节数;
    int flags:
        0,阻塞方式接收,当接收缓冲区中没有数据,则recv函数阻塞。当flag==0,recv等价于read函数。
        MSG_DONTWAIT:非阻塞方式接收,当接收缓冲区中没有数据,则recv函数不阻塞,且函数运行失败,
                      errno==EAGAIN or EWOULDBLOCK;
    struct sockaddr *src_addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。AF_INET-->man 7 ip
                            存储获取到的对端的地址信息,若不想获取对端地址信息,填NULL        
    socklen_t *addrlen:该指针指向的空间中需要填充上真实的地址信息结构体大小。
                        若src_addr参数填NULL,则该参数填NULL。                           
返回值:
    >0, 成功,接收到的字节数;
    =-1,函数运行失败,更新errno;
    =0, 对端关闭。(只有在流式套接字中使用)
    -----------------------------------------------------------------
注意:
recv(sockfd, buf, len, flags);等价于recvfrom(sockfd, buf, len, flags, NULL, NULL);
recv函数中的flags参数填0,等价于read函数。
  1. sendto
功能:将数据包发送给指定的对端。
原型:
       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);

       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
    int sockfd:通信套接字文件描述符;
    void *buf:指定要发送的数据首地址。
    size_t len:指定发送的字节数;
    int flags:
        0,阻塞方式发送,当发送缓冲区中满了,则send函数阻塞。当flag==0,send等价于write函数。
        MSG_DONTWAIT:非阻塞方式发送,当发送缓冲区满了,则send函数不阻塞,且函数运行失败,
                      errno==EAGAIN or EWOULDBLOCK;
    struct sockaddr *dest_addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。AF_INET-->man 7 ip
                                指定要将地址信息发送给谁,就填谁的地址信息。需要填充好地址信息。
    socklen_t addrlen:真实的地址信息结构体的大小。    
返回值:
    >0, 成功,发送的字节数;
    =-1,函数运行失败,更新errno;
    -----------------------------------------
   send(sockfd, buf, len, flags);  等价于  sendto(sockfd, buf, len, flags, NULL, 0);

UDPSER

#include<stdio.h>                          // 引入标准输入输出库
#include<sys/types.h>                      // 引入系统类型定义库
#include<sys/socket.h>                     // 引入套接字库
#include<unistd.h>                         // 引入Unix标准函数库
#include<arpa/inet.h>                      // 引入arpa网络库
#include<string.h>                         // 引入字符串处理库
#include<netinet/in.h>                     // 引入互联网地址族库

#define PORT 6666                          // 定义端口号为6666
#define IP "192.168.124.111"               // 定义IP地址为192.168.124.111

#define ERR_MSG(msg) {fprintf(stderr,"line:%d",__LINE__);perror(msg);} // 定义错误消息输出宏

int main(int argc, const char *argv[]) {
    int sfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字
    if (sfd < 0) {
        ERR_MSG("SOCKET");                   // 套接字创建失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("报式套接字创建成功, sfd = %d\n", sfd); // 套接字创建成功,打印套接字描述符

    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        ERR_MSG("setsockopt");               // 设置套接字选项失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("允许端口快速重用\n");            // 套接字选项设置成功,允许端口快速重用

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 设置地址族为AF_INET
    sin.sin_port = htons(PORT);              // 设置端口号
    sin.sin_addr.s_addr = inet_addr(IP);     // 设置IP地址

    if (bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
        ERR_MSG("bind");                     // 绑定失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("绑定成功\n");                    // 绑定成功,打印消息

    char buf[128] = "";                      // 定义缓冲区
    struct sockaddr_in cin;                  // 定义客户端地址结构
    socklen_t addrlen = sizeof(cin);         // 定义地址长度变量

    while (1) {
        bzero(buf, sizeof(buf));             // 清空缓冲区
        if (recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen) < 0) {
            ERR_MSG("recvfrom");             // 接收数据失败,打印错误消息
            return -1;                       // 返回错误码
        }
        printf("[%s: %d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf); // 打印接收到的消息

        strcat(buf, "*_*");                  // 追加字符到接收到的消息
        if (sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, sizeof(cin)) < 0) {
            ERR_MSG("sendto");               // 发送数据失败,打印错误消息
            return -1;                       // 返回错误码
        }
        printf("发送成功\n");                // 发送成功,打印消息
    }

    close(sfd);                              // 关闭套接字
    return 0;                                // 返回成功码
}

UDPCLI

#include<stdio.h>                          // 引入标准输入输出库
#include<sys/types.h>                      // 引入系统类型定义库
#include<sys/socket.h>                     // 引入套接字库
#include<unistd.h>                         // 引入Unix标准函数库
#include<arpa/inet.h>                      // 引入arpa网络库
#include<string.h>                         // 引入字符串处理库
#include<netinet/in.h>                     // 引入互联网地址族库

#define PORT 6666                          // 定义端口号为6666
#define IP "192.168.124.111"               // 定义IP地址为192.168.124.111

#define ERR_MSG(msg) {fprintf(stderr,"line:%d",__LINE__);perror(msg);} // 定义错误消息输出宏

int main(int argc, const char *argv[]) {
    int cfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字
    if (cfd < 0) {
        ERR_MSG("SOCKET");                   // 套接字创建失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("报式套接字创建成功, cfd = %d\n", cfd); // 套接字创建成功,打印套接字描述符

    int reuse = 1;
    if (setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        ERR_MSG("setsockopt");               // 设置套接字选项失败,打印错误消息
        return -1;                           // 返回错误码
    }
    printf("允许端口快速重用\n");            // 套接字选项设置成功,允许端口快速重用

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 设置地址族为AF_INET
    sin.sin_port = htons(PORT);              // 设置端口号
    sin.sin_addr.s_addr = inet_addr(IP);     // 设置IP地址

    char buf[128] = "";                      // 定义缓冲区
    struct sockaddr_in cin;                  // 定义客户端地址结构
    socklen_t addrlen = sizeof(cin);         // 定义地址长度变量

    while (1) {
        bzero(buf, sizeof(buf));             // 清空缓冲区
        printf("请输入>>> ");
        fgets(buf, sizeof(buf), stdin);      // 从标准输入读取数据
        buf[strlen(buf) - 1] = '\0';         // 去除输入末尾的换行符

        if (sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
            ERR_MSG("sendto");               // 发送数据失败,打印错误消息
            return -1;                       // 返回错误码
        }

        printf("发送成功\n");                // 发送成功,打印消息
        bzero(buf, sizeof(buf));             // 清空缓冲区

        if (recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen) < 0) {
            ERR_MSG("recvfrom");             // 接收数据失败,打印错误消息
            return -1;                       // 返回错误码
        }

        printf("[%s: %d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf); // 打印接收到的消息
    }

    close(cfd);                              // 关闭套接字
    return 0;                                // 返回成功码
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值