文章目录
- 1.TCP接口
- 1.1socket
- 文档
- 1.2listen
- 拓:端口号8080
- 1.3accept
- 拓:今天全局函数
- 1.4读写接口
- 1.5telnet
- 1.一个客户端
- 2.两个客户端
- 1.6ulimit -a
- 1.7常识回顾
- 1.8connect
- 1.9拓:客户端的ip和地址什么时候被分配?
- 1.10拓:为什么不推荐用using namespace std;
- 1.10send与sendto
- 1.11static_cast
1.TCP接口
1.1socket
文档
Linux下int socket(int domain, int type, int protocol);的第三个参数
在Linux下,socket 函数用于创建一个新的套接字,其原型如下:
c
int socket(int domain, int type, int protocol);
其中第三个参数 protocol 指定了套接字所使用的特定协议。大多数情况下,我们可以将此参数设置为0,让系统为我们自动选择合适的协议。但如果你需要指定特定的协议,那么这里就是你要设置的地方。
以下是一些常见的协议值:
IPPROTO_TCP: 使用TCP协议。
IPPROTO_UDP: 使用UDP协议。
IPPROTO_RAW: 使用原始IP协议。
IPPROTO_SCTP: 使用流控制传输协议(Stream Control Transmission Protocol)
当你为 protocol 参数指定非零值时,它必须与 domain 和 type 参数所指定的域和类型相匹配。例如,如果你为 domain 设置了 AF_INET(IPv4)并且为 type 设置了 SOCK_STREAM(流套接字,通常用于TCP),那么 protocol 参数应该设置为 IPPROTO_TCP。
需要注意的是,不是所有的 domain 和 type 组合都支持所有的 protocol。因此,在设置 protocol 参数之前,你应该查阅相关的文档或手册,以确保你的选择是有效的。
如果 protocol 参数设置为0,那么系统将自动为你选择合适的协议,这通常是最简单和最安全的选择。但如果你有特殊的需求或想要更精细地控制套接字的行为,那么你可以设置这个参数。
1.2listen
int listen(int sockfd, int backlog);的函数参数,函数功能,函数工作原理,返回值。
在Linux下,listen函数用于将套接字设置为监听模式,以接受来自客户端的连接请求。以下是关于listen函数的详细介绍:
函数参数
c
int listen(int sockfd, int backlog);
int sockfd:这是之前通过socket函数创建的套接字描述符。它标识了服务器将要监听的套接字。
int backlog:这是一个指定未完成连接队列长度的参数。当多个客户端同时尝试连接到服务器时,那些尚未被accept函数处理的连接请求将被放在队列中等待。backlog定义了队列中允许的最大连接数。一旦队列满了,新的连接请求可能会被拒绝。需要注意的是,这个值并不一定严格限制队列大小,它更多是一个提示给操作系统,实际的队列大小可能会根据系统实现有所不同。
函数功能
listen函数的主要功能是将套接字设置为监听状态,并准备接受客户端的连接请求。一旦套接字被设置为监听模式,它就可以使用accept函数来接受连接。
函数工作原理
当listen函数被调用时,它会通知内核开始监听来自客户端的连接请求。内核会维护一个队列来存储等待处理的连接请求。当客户端尝试连接到服务器时,内核会检查是否有可用的套接字描述符并创建一个新的连接条目放入队列中。如果队列已满,新的连接请求可能会被拒绝,具体行为取决于操作系统和网络协议栈的实现。
返回值
如果listen函数成功执行,它将返回0。如果发生错误,它将返回-1,并设置全局错误变量errno以指示错误原因。
示例错误情况
可能遇到的错误包括:
EBADF:提供的sockfd不是一个有效的文件描述符。
ENOTSOCK:提供的sockfd不是一个套接字文件描述符。
EOPNOTSUPP:提供的套接字不支持监听操作(例如,它可能是一个数据报套接字而不是流套接字)。
EINVAL:sockfd没有绑定到任何地址,或者backlog参数的值非法。
使用注意事项
在调用listen函数之前,套接字通常需要先通过bind函数绑定到一个地址,然后通过socket函数创建。而且,一旦套接字开始监听,通常会在一个循环中调用accept函数来不断接受新的连接。
listen函数是构建服务器应用程序时非常关键的一步,它使得服务器能够处理来自多个客户端的并发连接请求。
拓:端口号8080
Linux下网络tcp编程时,服务端或者客户端通常以8080端口启动,这个端口号有什么特殊意义吗?
在Linux下的网络TCP编程中,端口号8080并没有特殊的内在含义。它只是一个用户可以自由选择和使用的端口号。端口号在0到65535的范围内,其中0到1023是知名端口(也称为系统端口),通常被预留给一些特定的服务,如HTTP(80端口)、FTP(21端口)、SSH(22端口)等。而1024到49151是注册端口,通常被用户注册的服务使用,49152到65535则是动态端口或私有端口,用户可以自由使用。
在教学环境中,老师可能选择使用8080端口作为示例,有几个可能的原因:
避免冲突:8080端口不是知名端口,因此不太可能与已经运行的服务发生冲突。这样,学生可以在本地机器上轻松地启动和测试他们的TCP服务器或客户端,而无需担心端口占用问题。
易于记忆:8080端口相对容易记忆,这对于学生来说可能是一个优势,尤其是当他们需要手动输入端口号时。
习惯用法:在一些应用中,8080端口被用作默认的替代HTTP端口。例如,当80端口被其他服务占用时,一些web服务器会选择使用8080端口。因此,它在教学环境中也被广泛使用,以便与这些实际应用保持一致。
需要注意的是,尽管8080端口在教学环境中经常被用作示例,但在实际应用中,你应该根据你的具体需求和环境来选择适合的端口号。此外,如果你打算在互联网上公开你的服务,你应该确保你选择的端口号没有与任何已知的服务冲突,并且该端口在你的网络环境中是开放的。
netstat -antp:tcp
netstat 是一个命令行工具,用于显示网络连接、路由表、接口统计等网络相关信息。
在 netstat -antp 命令中:
-a 选项表示显示所有活动的网络连接和监听的套接字。
-n 选项表示以数字形式显示地址和端口号,而不是尝试解析为主机名、服务名等。
-t 选项表示只显示 TCP 连接。
-p 选项表示显示每个连接的进程ID和程序名称。
-l:
因此,netstat -antp 命令会列出所有活动的 TCP 连接和监听的 TCP 套接字,以数字形式显示地址和端口号,并尝试显示与每个连接相关联的进程ID和程序名称。
这个命令在排查网络问题、检查服务是否正常运行等方面非常有用。需要注意的是,为了获取与套接字关联的进程信息,你可能需要具有足够的权限(例如,使用 sudo 命令)。
另外,netstat 命令在某些现代的Linux发行版中可能已经被 ss 命令所取代或作为 iproute2 工具包的一部分。如果你的系统中没有 netstat 命令,你可以尝试使用 ss 命令或查找 iproute2 工具包来获取类似的功能。
netstat -nltp
netstat -nltp 命令在 Linux 系统中用于显示关于网络连接的统计信息。具体来说,这个命令的各个选项的含义如下:
-n:以数字形式显示地址和端口号,而不是尝试解析为主机名、服务名等。
-l:仅显示监听套接字(即等待传入连接的套接字)。
-t:仅显示 TCP 连接。
-p:显示每个连接的进程 ID 和程序名称。
因此,netstat -nltp 会列出系统上所有处于监听状态的 TCP 套接字,并以数字形式显示地址和端口号,同时还会显示每个套接字对应的进程 ID 和程序名称(如果可能的话)。
这个命令在诊断网络问题、检查服务是否正常运行以及查看哪些程序正在监听特定的端口等方面非常有用。
需要注意的是,为了查看套接字对应的进程信息,你可能需要以 root 用户身份运行这个命令,或者使用 sudo 来获取必要的权限。
还要注意的是,一些现代的 Linux 发行版可能默认没有安装 netstat 工具,而是推荐使用 ss 命令,它提供了类似的功能并且可能更加高效。如果你的系统中没有 netstat,你可以尝试使用 ss -nltp 来获取类似的信息。
1.3accept
在Linux下,accept函数用于从已完成连接队列中提取下一个连接请求,并返回一个新的套接字描述符,这个新的套接字描述符用于与该客户端进行通信。以下是关于accept函数的详细介绍:
函数参数
c
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int sockfd:这是服务器之前通过socket函数创建的套接字描述符,并且已经通过bind和listen函数绑定到地址并设置为监听状态。
struct sockaddr *addr:这是一个指向sockaddr结构体的指针,用于接收客户端的地址信息。如果此参数为NULL,则不返回客户端地址信息。
socklen_t *addrlen:这是一个指向套接字地址长度的指针。在调用accept之前,它应该设置为addr结构体的大小。函数返回时,它会被设置为实际返回的地址的长度。
函数功能
accept函数的主要功能是接受一个来自客户端的连接请求,并返回一个新的套接字描述符,这个新的套接字描述符用于和客户端之间的通信。原始的sockfd套接字描述符仍然用于监听新的连接请求。
函数工作原理
当listen函数被调用后,服务器套接字开始监听来自客户端的连接请求。一旦有客户端连接,内核会接受这个连接请求,并将连接信息放入已完成连接队列中。accept函数会阻塞调用线程(除非套接字被设置为非阻塞模式),直到有连接请求可用。当有连接请求时,accept函数会从队列中提取第一个连接,创建一个新的套接字描述符,并返回这个新的描述符给调用者。
返回值
如果accept函数成功,它会返回一个新的套接字描述符,这个描述符用于与客户端通信。如果发生错误,它会返回-1,并设置全局错误变量errno以指示错误原因。
示例错误情况
可能遇到的错误包括:
EBADF:提供的sockfd不是一个有效的文件描述符。
ENOTSOCK:提供的sockfd不是一个套接字文件描述符。
EOPNOTSUPP:sockfd没有处于监听状态。
EINVAL:sockfd是一个未绑定的套接字。
EWOULDBLOCK 或 EAGAIN:套接字被设置为非阻塞模式,且没有连接请求等待处理。
ECONNABORTED:一个已建立的连接被对端异常关闭。
EMFILE:进程已达到打开文件描述符的上限。
ENFILE:系统范围内打开文件描述符的数量已达到上限。
使用注意事项
accept函数通常用于服务器程序中,它必须在一个循环中调用以处理多个客户端连接。每次调用accept都会处理队列中的一个连接请求,并返回一个新的套接字描述符。原始的监听套接字描述符sockfd会继续等待新的连接请求。
注意,返回的新的套接字描述符不再继承原始套接字选项或属性。因此,如果需要,你可能需要为新的套接字描述符重新设置这些选项或属性。
拓:今天全局函数
静态全局函数的目的和作用
静态全局函数的主要目的和作用在于限制其作用域和链接属性。具体来说:
作用域限制:通过在函数定义前加上static关键字,将函数声明为静态全局函数,使其只在当前文件中可见,其他文件无法访问它们。这可以避免与其他文件中具有相同名称的全局函数发生命名冲突。
链接属性:静态全局函数具有内部链接属性,意味着它们只能在当前源文件中使用,无法被其他源文件引用。
此外,静态全局函数与静态成员函数在目的和作用上有所不同。静态成员函数不依赖于任何特定的对象实例,因此可以直接通过类名调用,而无需创建对象。静态成员函数主要用于访问和修改静态成员变量,这些变量在所有类对象之间共享,这使得静态成员函数适合用于执行与类相关的全局操作或共享数据的管理。
综上所述,静态全局函数的主要作用在于限制函数的作用范围,确保其在特定文件内的独立性和安全性,同时避免命名冲突。如需更多关于静态全局函数的信息,建议查阅相关编程书籍或咨询专业编程人员。
1.4读写接口
1.5telnet
1.一个客户端
在 Linux 下的终端(命令行界面)中,如果你尝试使用 telnet 命令连接到本地地址 127.0.0.1 的 8080 端口,首先你需要确保 telnet 客户端工具已经安装在你的系统中。在一些现代的 Linux 发行版中,telnet 可能不是默认安装的,因为它被视为一个较旧的工具,而 nc(netcat)或 socat 等工具更受欢迎。
如果你的系统中没有安装 telnet,你可以使用包管理器来安装它。例如,在基于 Debian 的系统(如 Ubuntu)中,你可以使用以下命令安装:
bash
sudo apt-get update
sudo apt-get install telnet
在基于 Red Hat 的系统(如 CentOS 或 Fedora)中,你可以使用:
bash
sudo yum install telnet
或者,如果你的系统使用 dnf:
bash
sudo dnf install telnet
安装完成后,你就可以使用 telnet 命令来连接到本地的 8080 端口了:
bash
telnet 127.0.0.1 8080
如果 8080 端口上有一个服务在监听(比如一个 web 服务器或其他任何 TCP 服务),你将通过 telnet 与该服务建立连接,并且可以在命令行界面中进行交互。如果端口上没有服务在监听,telnet 将会报告连接失败。
请注意,telnet 不提供加密,所以它不应用于需要安全通信的情况。对于加密的网络连接,通常会使用 SSH、HTTPS 或其他安全协议。
另外,127.0.0.1 是本地回环地址,它指向运行命令的同一台机器。如果你想要连接到网络上的另一台机器上的 8080 端口,你需要将 127.0.0.1 替换为那台机器的 IP 地址或主机名。
2.两个客户端
1.6ulimit -a
ulimit -a 是 Unix 和 Linux 系统中用于显示当前 shell 会话的所有资源限制的命令。这些资源限制可以包括打开的文件描述符的数量、可以创建的进程数、可以使用的最大虚拟内存量等。ulimit 命令允许用户查看和设置这些限制。
当你运行 ulimit -a 时,你会看到一系列的资源类型以及它们的当前限制值。以下是一些常见的资源类型及其描述:
core file size:核心文件大小的最大限制(KB)。当程序崩溃时,它可能会生成一个核心转储文件(core dump),这个选项限制了这种文件的大小。
data seg size:进程数据段的最大大小(KB)。
scheduling priority:进程的调度优先级。
file size:可以创建的最大文件大小(KB)。
pending signals:可以排队的最大信号数。
max locked memory:一个进程可以锁定的最大物理内存量(KB)。
max memory size:一个进程的最大虚拟内存(KB)。
open files:一个进程可以同时打开的最大文件数。
pipe size:管道的最大大小(512字节块)。
POSIX message queues:POSIX 消息队列的最大字节数。
real-time priority:实时调度的优先级。
stack size:进程栈的最大大小(KB)。
cpu time:CPU 时间的最大限制,包括用户 CPU 时间和系统 CPU 时间。
max user processes:用户可以拥有的最大进程数。
virtual memory:进程的最大虚拟内存(KB)。
file locks:一个进程可以持有的最大文件锁数。
请注意,不是所有的系统都支持所有的资源限制,而且某些限制可能只对超级用户(root)可用。此外,具体的限制值可能因系统配置和策略而异。
通过 ulimit 命令,系统管理员和用户可以在一定程度上控制进程的资源使用,以防止资源耗尽或滥用。
1.7常识回顾
Linux:如果父进程关闭了一个fd,对子进程有影响吗
在Linux中,当一个父进程创建了一个子进程(通过fork()系统调用),子进程会获得父进程的一个副本,这包括父进程的文件描述符(file descriptors,简称fd)。这些文件描述符在子进程中是独立的,也就是说,对父进程中的一个文件描述符所做的修改(如关闭、重定向等)不会直接影响到子进程中的对应文件描述符。
因此,如果父进程关闭了一个文件描述符,这个操作对子进程是没有影响的。子进程仍然保留着它自己的那份文件描述符的副本,直到它自己决定关闭它。
但是,需要注意的是,如果父进程和子进程都试图操作同一个文件(通过它们各自的文件描述符),并且父进程先关闭了它的文件描述符,那么子进程仍然可以通过它的文件描述符来操作该文件。然而,如果该文件是一个临时文件,并且父进程是唯一持有该文件引用的进程,那么当父进程关闭其文件描述符并退出时,该文件可能会被删除,这样即使子进程仍然有文件描述符指向它,也可能无法再成功地进行读写操作。
另外,如果父进程和子进程共享了某些资源(例如通过管道、套接字等),那么父进程关闭其端的操作可能会影响到子进程。这取决于具体的通信机制和资源的性质。例如,如果父进程关闭了一个管道的写端,子进程从该管道的读端读取时可能会收到一个EOF信号,表明没有更多的数据可以读取。
如果父进程关闭了一个tcp套接字,对子进程有影响吗
在Linux中,如果父进程关闭了一个TCP套接字(socket),这个操作对子进程是否有影响,取决于子进程是否继承了该套接字的文件描述符。
当父进程通过fork()创建子进程时,子进程会复制父进程的文件描述符表,包括套接字文件描述符。这意味着子进程将拥有与父进程相同的套接字文件描述符的副本。然而,这两个文件描述符在内核中是独立的,对其中一个文件描述符的操作(如关闭)不会影响另一个。
因此,如果父进程关闭了它的套接字文件描述符,子进程中的对应套接字文件描述符仍然保持打开状态,除非子进程自己也显式地关闭了它。子进程可以继续使用它的套接字文件描述符进行网络通信,直到它决定关闭它或进程终止。
需要注意的是,如果父进程和子进程都试图操作同一个套接字,并且父进程关闭了它的套接字,那么只有父进程不能再通过该套接字进行通信。子进程仍然可以使用它的套接字副本进行通信,直到它也关闭该套接字或进程结束。
此外,还要考虑到套接字可能关联的资源,如内存缓冲区、连接状态等。这些资源在内核中由套接字管理,而不是由文件描述符本身管理。因此,即使父进程关闭了它的套接字文件描述符,与套接字关联的资源可能仍然存在于内核中,直到所有相关的文件描述符都被关闭且套接字被完全销毁。
综上所述,父进程关闭一个TCP套接字对子进程本身没有影响,子进程仍然可以使用它的套接字副本进行通信。但是,如果套接字关联的资源需要被清理或释放,那么必须确保所有相关的文件描述符都被关闭。
1.8connect
在Linux中,connect函数用于建立客户端与服务器之间的连接。以下是关于connect函数的详细介绍:
函数参数
c
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int sockfd:这是由socket函数返回的套接字描述符,代表客户端的套接字。
const struct sockaddr *addr:这是一个指向sockaddr结构体的指针,该结构体包含了服务器的地址信息,包括IP地址和端口号。通常,这个结构体是sockaddr_in类型的,用于IPv4地址。
socklen_t addrlen:这是一个值,表示addr指向的地址结构体的长度。在调用connect之前,应该设置为addr指向的结构体的大小(例如,对于sockaddr_in结构体,其值通常为sizeof(struct sockaddr_in))。
函数功能
connect函数的主要功能是尝试与由addr参数指定的服务器建立连接。如果连接成功建立,则套接字sockfd可以用于与服务器进行通信。
函数工作原理
当调用connect函数时,客户端会向指定的服务器发送一个连接请求。这个过程通常涉及TCP的三次握手:
客户端发送一个SYN包到服务器,请求建立连接。
服务器收到SYN包后,回复一个SYN-ACK包给客户端,表示同意建立连接。
客户端收到SYN-ACK包后,发送一个ACK包给服务器,完成三次握手过程,连接建立成功。
如果服务器不可达,或者由于其他原因连接请求不能被接受,connect函数会返回错误。
返回值
如果connect函数成功建立了连接,它会返回0。如果连接失败,它会返回-1,并设置全局变量errno以指示错误原因。可能的错误包括:
ECONNREFUSED:服务器拒绝了连接请求。
ETIMEDOUT:连接请求超时。
EHOSTUNREACH:服务器主机不可达。
ENETUNREACH:网络不可达。
EADDRINUSE:本地地址已在使用中。
EINPROGRESS:连接正在进行中(非阻塞模式下)。
注意事项
在调用connect之前,通常需要先调用socket函数创建一个套接字,并可能还需要调用bind函数(尽管对于客户端套接字,bind通常不是必需的)。
connect函数是阻塞的,意味着它会等待连接建立完成或发生错误。如果你希望connect在连接建立时不阻塞调用线程,你可以设置套接字为非阻塞模式,这可以通过fcntl函数或使用O_NONBLOCK标志在socket或open调用时完成。
connect函数通常用于TCP连接,因为UDP是无连接的协议,不需要建立连接。对于UDP,你可以直接使用sendto和recvfrom函数来发送和接收数据。
1.9拓:客户端的ip和地址什么时候被分配?
客户端的IP地址通常是在其连接到网络时由网络中的设备(如路由器或DHCP服务器)分配的。这个过程通常发生在客户端设备启动并尝试接入网络时。具体来说,如果网络使用DHCP(动态主机配置协议),那么客户端会向DHCP服务器发送请求,请求分配一个可用的IP地址。DHCP服务器会响应这个请求,并分配一个IP地址给客户端,同时可能还会提供其他网络配置信息,如子网掩码、默认网关和DNS服务器地址等。
另一方面,客户端的端口号是在应用层分配的。当客户端的应用程序创建一个套接字(socket)并准备与服务器建立连接时,操作系统会自动为这个套接字分配一个未被使用的端口号。这个端口号用于在通信过程中唯一标识该客户端的连接。通常,对于TCP或UDP这样的传输层协议,客户端不需要显式指定端口号,操作系统会自动处理这一部分。
需要注意的是,客户端的IP地址和端口号在网络通信中起着重要的作用。IP地址用于在网络中定位客户端设备,而端口号则用于在客户端设备上标识特定的应用程序或服务。因此,确保客户端正确获取IP地址和端口号对于建立成功的网络连接至关重要。
1.10拓:为什么不推荐用using namespace std;
不推荐在头文件中使用 using namespace std; 的原因主要有以下几点:
命名冲突:std 命名空间包含了大量的函数和对象,如 std::cout、std::vector 等。如果在头文件中使用 using namespace std;,那么任何包含这个头文件的源文件都会自动引入这些名称。这可能导致命名冲突,尤其是当源文件中定义了与 std 命名空间中的名称相同的变量或函数时。
可读性降低:使用 using namespace std; 会使得代码中的 std 命名空间中的名称变得不明确。这可能会导致读者在阅读代码时不清楚某些名称是来自 std 命名空间还是其他地方。明确使用 std:: 前缀可以提高代码的可读性。
维护困难:如果某个未来的 std 命名空间版本添加了一个新的名称,而这个名称与你的代码中的某个名称冲突,那么你的代码可能会突然出现问题。为了避免这种潜在的维护问题,最好避免在头文件中使用 using namespace std;。
最佳实践:许多编程规范和最佳实践都建议避免在头文件中使用 using namespace 声明。这是为了保持代码的清晰、一致和可维护。
然而,值得注意的是,在源文件的开始部分使用 using namespace std; 是可以接受的,前提是你清楚这样做不会导致命名冲突,并且你的代码只在这个源文件中使用。这样,你可以享受到使用 std 命名空间中的名称的便利,同时避免了在头文件中使用它所带来的问题。
1.10send与sendto
在Linux下,send函数是用于在已连接的套接字上发送数据的系统调用。下面是对send函数的详细介绍:
函数参数
c
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
int sockfd:已连接的套接字描述符。这通常是通过socket和connect函数得到的。
const void *buf:指向要发送数据的缓冲区的指针。
size_t len:要发送数据的字节数。
int flags:控制发送行为的标志。常见的标志包括MSG_DONTWAIT(非阻塞发送)和MSG_OOB(发送带外数据)。
函数功能
send函数的功能是在已连接的套接字上发送数据。它通常用于TCP连接,因为TCP是面向连接的协议。
函数工作原理
当调用send函数时,内核会尝试将数据从用户空间缓冲区复制到内核空间的发送缓冲区。如果套接字处于非阻塞模式且发送缓冲区已满,send可能会返回EAGAIN或EWOULDBLOCK错误。如果套接字处于阻塞模式,send将阻塞直到有空间可用或发送完成。
一旦数据被复制到内核发送缓冲区,发送操作就被认为是成功的,即使数据可能还没有实际离开主机。实际的数据传输(即数据从内核发送缓冲区发送到网络上)是异步进行的,并且可能由内核在稍后的时间点完成。
返回值
send函数返回实际发送的字节数。这通常等于len参数的值,除非发生了错误或发送了少于请求字节数的数据。在出错的情况下,send返回-1,并设置全局变量errno以指示错误原因。
与sendto的异同
sendto函数与send函数类似,但主要用于无连接套接字(如UDP)。它的函数原型如下:
c
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sendto和send的主要区别在于:
目标地址:sendto需要显式提供目标地址(通过dest_addr和addrlen参数),而send则假定目标地址已经在之前的connect调用中指定。
套接字类型:send通常用于已连接的套接字(如TCP),而sendto用于无连接的套接字(如UDP)。
连接状态:使用send之前,套接字通常需要调用connect来建立连接。而sendto不需要先建立连接,每次调用时都可以指定不同的目标地址。
两者在功能上都是发送数据,但适用于不同类型的套接字和不同的使用场景。
1.11static_cast
static_cast 是 C++ 中的一个类型转换运算符,它用于执行安全的、显式的类型转换。使用 static_cast 可以确保程序员明确知道他们正在进行的转换,并且编译器可以帮助检查这种转换是否安全。
以下是一些 static_cast 的常见用法:
基本数据类型之间的转换:
cpp
double d = 3.14;
int i = static_cast(d); // i 现在是 3
类层次结构中的向上和向下转换:
在类继承层次结构中,static_cast 可以用于向上转换(从派生类到基类)和向下转换(从基类到派生类,但通常不安全,除非你确定对象确实是目标类型)。
cpp
class Base {};
class Derived : public Base {};
Derived d;
Base* b = static_cast<Base*>(&d); // 向上转换,安全
Derived* dd = static_cast<Derived*>(b); // 向下转换,通常不安全
空指针和整数之间的转换:
static_cast 可以用于将空指针转换为整数(例如,nullptr 或 0),或者将整数 0 或 1 转换为空或非空指针。
void 到其他类型的转换*:
如果你有一个 void* 指针,并且你知道它实际上指向某种特定类型的对象,你可以使用 static_cast 将其转换回那种类型。
需要注意的是,虽然 static_cast 提供了一种进行类型转换的方法,但它并不总是安全的。特别是当进行向下转换(从基类到派生类)时,除非你确定对象确实是目标类型,否则这种转换可能会导致未定义的行为。在这种情况下,使用 dynamic_cast(如果可能的话)可能更安全,因为它会在转换失败时返回空指针。
总之,static_cast 是一种强大的工具,但应该谨慎使用,确保转换是安全的。