网络 套接字编程 TCP、UDP

网络是大端

发数据从低地址发, 先发的是 高位的数据。

收数据从高位收,先收到的数据存放到低地址。

TCP 是 流式的 所用套接字也是流式的

文件描述符

socket 是 IP 加 端口号



用到的函数:

        int socket(int domain, int type, int protocol);
       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
       int inet_aton(const char *cp, struct in_addr *inp);
       in_addr_t inet_addr(const char *cp);
       in_addr_t inet_network(const char *cp);
       char *inet_ntoa(struct in_addr in);
       struct in_addr inet_makeaddr(int net, int host);
       in_addr_t inet_lnaof(struct in_addr in);


查找头文件中的类型定义

grep -ER



练习代码:【服务器 阻塞式的 只能处理一个请求】

/******
 * 阻塞式的
 *
 * **********/
#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include<string.h>
#include <arpa/inet.h>
//#include <unistd.h>
#define  _PORT_ 9999
#define _BACKLOG_ 10   // 监听端口 队列长度
int main()
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        printf("create socket error, error is : %d, errstring is : %s\n", errno, strerror(errno));
        return 1;
    }
    struct sockaddr_in server_socket;
    struct sockaddr_in client_socket;
    bzero(&server_socket, sizeof(server_socket));
    server_socket.sin_family = AF_INET;
    server_socket.sin_addr.s_addr = htonl(INADDR_ANY);
    server_socket.sin_port = htons(_PORT_);
    if (bind(sock, (struct sockaddr*)&server_socket, sizeof(struct sockaddr_in)) < 0)
    {
        printf("bind error, error code is : %d, errno string is :%s\n", errno, strerror(errno));
        close(sock);
        return 2;
    }
    if (listen(sock, _BACKLOG_) < 0)
    {
        printf("listen error , errno code is :%d, errno string is :%s\n", errno, strerror(errno));
        close(sock);
        return 3;
    }
    printf("bind and listen success, wait accept...\n");
    while (1)
    {
        socklen_t  len = 0;
        int client_sock = accept(sock, (struct sockaddr *)&client_sock, &len);
    if (client_sock < 0)
    {
        perror("accept");
        close(sock);
        return 4;
    }
    char buf[1024];
    memset(buf, ‘\0‘, sizeof(buf));
    inet_ntop(AF_INET, &client_socket.sin_addr, buf, sizeof(buf));
    printf("get connect, ip is :%s port is : %d\n", buf,ntohs(client_socket.sin_port));
    while (1)
    {
        memset(buf, ‘\0‘, sizeof(buf));
        read(client_sock, buf, sizeof(buf) - 1);
        printf("client : %s\n", buf);
        printf("server :");
        memset(buf, ‘\0‘, sizeof(buf));
    //    fgets(buf, sizeof(buf), stdin);
    //    buf[strlen(buf) - 1] = ‘\0‘;
        write(client_sock, buf, strlen(buf)+1);
        printf("please wait...\n");
    }
    }
close(sock);
return 0;
}

运行:

查看监听状态

[[email protected] ~]$ netstat -nltp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 0.0.0.0:9999                0.0.0.0:*                   LISTEN      10091/./server_1_si
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      -                   
tcp        0      0 192.168.122.1:53            0.0.0.0:*                   LISTEN      -                   
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      -                   
tcp        0      0 0.0.0.0:60790               0.0.0.0:*                   LISTEN      -                   
tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      -                   
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      -                   
tcp        0      0 :::47946                    :::*                        LISTEN      -                   
tcp        0      0 :::111                      :::*                        LISTEN      -                   
tcp        0      0 :::22                       :::*                        LISTEN      -                   
tcp        0      0 :::23                       :::*                        LISTEN      -                   
tcp        0      0 ::1:631                     :::*                        LISTEN      -                   
tcp        0      0 ::1:25                      :::*                        LISTEN      -


测试的时候 用 telnet

telnet  CTRL+]  就可以进入命令行了



问题描述:服务器与 客户端连接后  马上 kill掉服务器  然后又启动服务器 出现 Address already in use的错误 linux中等上半分钟 就可以启动服务器了

[[email protected] tcp]$ ./server_1_simple

bind and listen success, wait accept...

get connect, ip is :13.0.0.0 port is : 1032

^C

[[email protected] tcp]$ ./server_1_simple

bind error, error code is : 98, errno string is :Address already in use

原因:

解决方法:

练习代码:【客户端 】

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SERVER_PORT 9999
//#define SERVER_IP "192.168.0.1"
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("usage:server  IP\n");
        return 1;
    }
    char* str_server_ip = argv[1];
    char buf[1024];
    memset(buf, ‘\0‘, sizeof(buf));
    struct sockaddr_in server_sock;
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&server_sock, sizeof(server_sock));
    server_sock.sin_family = AF_INET;
    //inet_pton(AF_INET, str_server_ip, &server_sock.sin_addr);
    //别的 写法
    server_sock.sin_addr.s_addr = inet_addr(str_server_ip);
    server_sock.sin_port = htons(SERVER_PORT);
    int ret = connect(sock,(struct sockaddr*)&server_sock, sizeof(server_sock));
    if (ret < 0)
    {
        perror("connect");
        return 2;
    }
    printf("connect success...\n");
    while (1)
    {
        printf("client:>");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = ‘\0‘; // 把‘\n‘ 置成‘\0‘
        write(sock, buf, strlen(buf) + 1);
        printf("please wait ...\n");
    }
    close(sock);
    return 0;
}


练习代码:【线程的方式 服务器代码:可以同时处理多个客户请求】

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define LISTEN_QUEUE_NUM 50
void* handler_data(void* arg)
{
    int client_socket = (int)arg;
    printf("create a new thread...new socket:%d\n", client_socket);
    char buf[1024];
    while (1)
    {
        ssize_t _s = read(client_socket, buf, sizeof(buf) - 1);
        if (_s > 0)
        {
            buf[_s] = ‘\0‘;
            printf("client %s\n", buf);
            write(client_socket, buf, strlen(buf));
        }
        else if (_s == 0) // client close
        {
            printf("client is close\n");
             break;
        }
        else
        {
            break;
        }
    }
    close(client_socket);
    return (void*)0;
}
int main()
{
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(9999);
    local.sin_addr.s_addr = htonl(INADDR_ANY);
//    local.sin_addr.s_addr = inet_addr("192.168.1.1");
//
  if (bind(listen_sock,(struct sockaddr*)&local, sizeof(local)) < 0)
  {
      perror("bind");
      exit(2);
  }
    if (listen(listen_sock, LISTEN_QUEUE_NUM) < 0)
    {
          perror("listen");
          exit(3);
    }
  struct sockaddr_in peer;
  socklen_t len = sizeof(peer);
 // signal(SIGCHLD, collect_child);
  while (1)
  {
      int new_fd = accept(listen_sock, (struct sockaddr*)&peer, &len);
      if (new_fd > 0)
      {
          pthread_t tid ;
          pthread_create(&tid, NULL, handler_data, (void*)new_fd);
          pthread_detach(tid);
      }
  }
  close(listen_sock);
  return 0;
}

练习代码:【客户端代码】

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void usage(const char *proc)
{
    printf("%s [ip] [port]\n", proc);
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        return 3;
    }
    int conn = socket(AF_INET, SOCK_STREAM, 0);
    if (conn < 0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in remote;
    remote.sin_family = AF_INET;
    remote.sin_port = htons(atoi(argv[2]));
    remote.sin_addr.s_addr = inet_addr(argv[1]);
    if (connect(conn, (struct sockaddr*)&remote, sizeof(remote)) < 0)
    {
        perror("connect");
        return 2;
    }
    char buf[1024];
    while(1)
    {
        ssize_t _s = read(0, buf, sizeof(buf) - 1);
        if (_s > 0)
        {
            //buf[_s - 1] = ‘\0‘;// 去掉\n
            buf[_s] = ‘\0‘;
            write(conn, buf, strlen(buf));
            read(conn , buf, sizeof(buf) - 1);
            printf("server echo : %s\n", buf);
        }
        else
        {
            perror("read ----");
            break;
        }
    }
    return 0;
}


练习代码:【服务器 是 进程实现的】

#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void collect_child(int sig)
{
    while (waitpid(-1, 0, WNOHANG) > 0)
    {
        printf("collect child done...\n");
    }
}
int main()
{
    int listen_sock = socket(AF_INET, SOCK_STREAM , 0);
    if (listen_sock < 0)
    {
        perror("listen ");
        exit(1);
    }
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(9999);
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        perror("bind ");
        exit(2);
    }
    if (listen(listen_sock, 20) < 0) // backlog 20
    {
        perror("listen");
        exit(3);
    }
    struct sockaddr_in peer;
    socklen_t len = sizeof(peer);
    signal(SIGCHLD, collect_child); //////////////////////
    while (1)
    {
        int new_fd = accept(listen_sock, (struct sockaddr*)&peer, &len);
        if (new_fd < 0)
        {
            perror("accept");
            exit(4);
        }
        pid_t id = fork();
        if (id == 0)
        {
            // child
            printf("fork done... pid : %d, get a new client: socket >%s:%d\n", getpid(), inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
            char buf[1024];
            while (1)
            {
                ssize_t _s = read(new_fd, buf, sizeof(buf) - 1);
                if (_s > 0)
                {
                    buf[_s] = ‘\0‘;
                    printf("client: %s\n", buf);
                    write(new_fd,buf, strlen(buf) );
                }
                else if (_s == 0)
                {
                    printf("client is close\n");
                    break;
                }
                else
                {
                    break;
                }
            }
            close(new_fd);
            exit(0);
        }
        else
        {
            // father
            close(new_fd);
        }
    }
}

练习代码:【客户端】

#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void usage(const char *proc)
{
    printf("%s [ip] [port]\n", proc);
}
int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        return 3;
    }
    int conn = socket(AF_INET, SOCK_STREAM, 0);
    if (conn < 0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in  remote;
    remote.sin_family = AF_INET;
    remote.sin_addr.s_addr = inet_addr(argv[1]);
    remote.sin_port = htons(atoi(argv[2]));
    if (connect(conn, (struct sockaddr*)&remote, sizeof(remote)) < 0)
    {
        perror("connect");
        return 2;
    }
    char buf[1024];
    while (1)
    {
        ssize_t _s = read(0, buf, sizeof(buf) - 1);
        if (_s > 0)
        {
            buf[_s] = ‘\0‘;
            write(conn, buf, strlen(buf));
            read(conn, buf, sizeof(buf) - 1);
            printf("server echo:%s\n", buf);
        }
        else
        {
            exit(55);
        }
    }
    close(conn);
    return 0;
}

运行结果:

[[email protected] tcp3]$ netstat -alpt
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
.............................................
tcp        0      0 localhost:distinct          localhost:56683             ESTABLISHED 3451/./server       
tcp        0      0 localhost:56683             localhost:distinct          ESTABLISHED 3450/./client       
tcp        1      0 192.168.174.158:60487       ip-42-99-128-139.pacne:http CLOSE_WAIT  2969/clock-applet   
tcp        0      0 localhost:56682             localhost:distinct          ESTABLISHED 3411/./client       
tcp        0      0 localhost:distinct          localhost:56682             ESTABLISHED 3412/./server


udp编程

UDP基础知识

UDP(User Datagram Protocol,用户数据报协议)是一个简单的、面向数据报的无连接协议,提供了快速但不一定可靠的传输服务。

  UDP与TCP相比主要有以下区别。

    1.UDP速度比TCP快

      由于UDP不需要先与对方建立连接,也不需要传输确认,因此其数据传输速度比TCP快得多。

    2.UDP有消息边界

      使用UDP不需要考虑消息边界问题,使用上比TCP简单

    3.UDP可以一对多传输

      利用UDP可以使用广播或组播的方式同时向子网上的所有客户发送信息。这一点也比TCP方便。

    4.UDP可靠性不如TCP

      与TCP不同,UDP并不提供数据传送的保证机制。如果在从发送方到接收方的传递过程中出现数据报的丢失,协议本身并不能做出任何检测或提示。因此,通常人们把UDP称为不可靠的传输协议。

    5.UDP不像TCP那样能保证有序传输

      UDP不能确保数据的发送和接收顺序。对于突发性的数据报,有可能会乱序。事实上,UDP的这种乱序性基本上很少出现,通常只会在网络非常拥挤的情况下才有可能发生

UDP编程

1)创建socket时,数据格式为:SOCK_DGRAM(数据块)

2)数据收发用recvfrom和sendto

ssize_t recvfrom(int socket,void *restrict buffer,size_t length,int flags,struct sockaddr * restrict address,socklen_t *restrict address_len);

restrict:类型限定符,限定约束指针。表明该指针是访问这个数据队形的唯一的方式

补充一点:

void *memcpy( void * restrict dest ,const void * restrict src,sizi_t n) 这是一个很有用的内存复制函数,由于两个参数都加了restrict限定,所以两块区域不能重叠,即 dest指针所指的区域,不能让别的指针来修改,即src的指针不能修改. 相对应的别一个函数 memmove(void *dest,const void * src,size_t)则可以重叠。

socket:  已连接的套接字

buffer:接收数据的缓冲区

length:缓冲区长度

flags  :调用操作方式

address:指向装有源地址的缓冲区(传出型参数)

address_len:指向源地址缓冲区的实际长度(传入传出型参数)

ssize_t sendto(int socket,const void*buffer,size_t length,int flags,struct sockaddr* dest_addr,socklen_t len);

socket:已连接套接字

buffer:包含待发送数据的缓冲区

length:buffer缓冲区数据的长度

flags:调用方式标志位

dest_addr:指向目的套接字的地址

len:dest_addr所指地址的长度

练习代码:【服务器 】

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BF_SIZE 1024
int main(const int argc,const  char* argv[])
{
    if (argc != 3)
    {
        printf("please enter %s [ip] [port]\n",argv[0]);
        exit(1);
    }
    int fd = socket(AF_INET, SOCK_DGRAM,0 );//SOCK_DGRAM 数据报套接口
    if (fd < 0)
    {
        perror("socket");
        exit(2);
    }
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = inet_addr(argv[1]);
    local.sin_port = htons(atoi(argv[2]));
    if (bind(fd, (struct sockaddr*)&local, sizeof(local)) <  0)
    {
        perror("bind");
        return 2;
    }
    struct sockaddr_in remote;
    socklen_t len =  sizeof(remote);
    while (1)
    {
        char buf[BF_SIZE];
        memset(buf, ‘\0‘, sizeof(buf));
        ssize_t _s = recvfrom(fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&remote, &len);// 注意len取地址 sendto 中不用
        if (_s > 0)
        {
            printf("client :[ip:%s][port:%d] %s", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), buf);
        }
        else if (_s == 0)
        {
            printf("read done...\n");
        }
        else
        {
            break;
        }
    }
    close(fd);
    return 0;
}

练习代码:【客户端】

#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
int main(const int argc,const char* argv[] )
{
    if (argc != 3)
    {
        printf("please enter %s [ip] [port]\n", argv[0]);
        return 1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        perror("socket");
        return 2;
    }
    int port = atoi(argv[2]);
    struct sockaddr_in remote;
    remote.sin_family = AF_INET;
    remote.sin_addr.s_addr = inet_addr(argv[1]);
    remote.sin_port = htons(port);
    while (1)
    {
        char buf[1024];
        ssize_t _s = read(0, buf, sizeof(buf) - 1);
        if (_s > 0)
        {
            buf[_s] = ‘\0‘;
        }
        _s = sendto(fd, buf, strlen(buf),0, (struct sockaddr*)&remote, sizeof(remote));
    }
    return 0;
}

运行:

[[email protected] ~]$ netstat -atup
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
.......
udp        0      0 localhost:distinct32        *:*                                     4671/./server       
udp        0      0 *:60703                     *:*                                     4673/./client
时间: 2024-11-03 20:45:24

网络 套接字编程 TCP、UDP的相关文章

Linux 套接字编程 - TCP连接基础

第五章的内容,实现一个echo服务器和对应的客户端,主要收获: 0. TCP socket编程主要基本步骤 1. SIGCHLD信号含义(子进程退出时向父进程发送,提醒父进程对其状态信息进行一个获取),waitpid 和 wait在使用上的差异,前者可以配置参数设定为非阻塞方式调用,更加灵活. 2. 信号处理函数与其过程(尤其是信号发生后不列队这个性质),相同的信号多次发生(间隔非常接近的话)可能仅会调用一次信号处理函数 3. 信号处理对慢系统(阻塞)调用如accept等的影响(如果信号处理设置

linux网络环境下socket套接字编程(UDP文件传输)

今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中,如果我们使用TCP传输,会造成传输速度较慢的情况,所以我们在进行文件传输的过程中,最好要使用UDP传输. 在其中,我们需要写两个程序,一个客户端,一个服务端,在一个终端中,先运行服务端,在运行客户端,在服务端和客户端都输入IP地址和端口号,注意服务端和客户端的端口号要相同,然后选择功能,在linux

Python网络编程—socket套接字编程(UDP)

套接字介绍 1.套接字 : 实现网络编程进行数据传输的一种技术手段 2.Python实现套接字编程:import socket 3.套接字分类 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案.(面向连接--tcp协议--可靠的--流式套接字) 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案.(无连接--udp协议--不可靠--数据报套接字) UDP套接字编程 服务端流程 1.创建数据报套接字 sockfd = socket

套接字编程--TCP

一.socket编程 socket本身有"插座"的意思,因此用来描述网络连接的一对一关系."在TCP/IP协议中,"TP地址+TCP或端口号"唯一标识网络通讯中的一个进程,"IP地址+端口号"就称为socket.(socket就像当于文件一样,客户端通过往里面写数据,服务器端就从里面读取数据,socket 就是用来做载体的).为TCP/TP协议设计的应用层编程接口称为socketAPI. 二.网络字节序 内存中的多字节数据相对于内存地址

Socket(套接字) IP TCP UDP HTTP

Socket(套接字) (转)什么是套接字(Socket)? 应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题.多个TCP连接或多个应用程序进程可能需要 通过同一个TCP协议端口传输数据.为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)(socket是操作系统提供出来的接口)的接口,区分不同应用程序进程间的网络通信和连接.生成套接字,主要有3个参数:通信的目的IP地址.使用的传输 层

套接字编程 ---- TCP协议

一.套接字(socket) 套接字socket: ip地址 + port端口号.在TCP/IP协议中,它唯一标识网络通讯中的一个进程. 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socketpair就唯一标识一个连接. socket本身有"插座"的意思,因此用来描述网络连接的 一对一关系. TCP/IP协议规定,网络数据流应采用 大端字节序,即 (内存)低地址高字节(数据). 二.TCP_SOCKET 相关 TCP 协议  ----  

网络编程[第二篇]基于udp协议的套接字编程

udp协议下的套接字编程 一.udp是无链接的    不可靠的 而上篇的tcp协议是可靠的,会有反馈信息来确认信息交换的完成与否 基于udp协议写成的服务端与客户端,各司其职,不管对方是否接收到信息,只需自己发送了即可 二.客户端 import socket #买手机 -- 套接字家族 | 端口协议 phone = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #服务端地址 ip_port = ('127.0.0.1',8001) #实现多次发送

探索UDP套接字编程

UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务,而TCP提供面向流.提供可靠数据服务.注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了. 典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等:服务端不接收来自客户端的连接,而只是调用recvfr

【转】 探索UDP套接字编程

UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务,而TCP提供面向流.提供可靠数据服务.注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了. 典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等:服务端不接收来自客户端的连接,而只是调用recvfr