赞
踩
TCP是一种面向连接的协议,它通过三次握手建立连接,然后在连接上进行可靠的数据传输。TCP使用序列号和确认应答(ACK)来保证数据的可靠传输,通过滑动窗口和拥塞控制算法进行流量控制和拥塞控制。
相比于TCP,UDP是一种更简单的协议。UDP是无连接的,它直接在IP协议之上发送数据报,不提供数据的可靠传输、流量控制或拥塞控制。因此,UDP的延迟和开销较小,适用于对实时性要求高的应用,如语音和视频通信。
在TCP和UDP通信中,数据是从客户端流向服务器的。客户端首先建立连接(TCP)或直接发送数据报(UDP),然后服务器接收并处理这些数据,可能会返回响应给客户端。在TCP通信中,数据的流动是双向的,客户端和服务器都可以发送数据和接收数据。在UDP通信中,数据的流动也是双向的,但是由于UDP是无连接的,客户端和服务器可以独立地发送和接收数据。
在Linux网络服务器编程中,我们使用socket来实现TCP和UDP通信。以下是TCP和UDP的socket使用示例:
服务器端:
#include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <string.h> #include <iostream> int main() { int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = INADDR_ANY; bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); listen(server_fd, 5); while (true) { struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len); char buffer[1024]; ssize_t read_len = read(client_fd, buffer, sizeof(buffer) - 1); buffer[read_len] = '\0'; std::cout << "Received: " << buffer << std::endl; write(client_fd, buffer, strlen(buffer)); close(client_fd); } close(server_fd); return 0; }
客户端:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <iostream> int main() { int client_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); const char *message = "Hello, Server!"; write(client_fd, message, strlen(message)); char buffer[1024]; ssize_t read_len = read(client_fd, buffer, sizeof(buffer) - 1); buffer[read_len] = '\0'; std::cout << "Received: " << buffer << std::endl; close(client_fd); return 0; }
在Linux网络编程中,socket()
, sockaddr_in
结构体和相关常量都是用于创建和配置套接字的关键组件。以下是上面代码的含义和用法:
AF_INET
:这是一个地址族(Address Family)常量,表示我们使用的是IPv4协议。在创建套接字时,需要指定地址族以确定使用哪种协议。另一个常见的地址族是AF_INET6
,表示使用IPv6协议。
SOCK_STREAM
:这是一个套接字类型(Socket Type)常量,表示我们使用的是面向连接的、可靠的字节流。在TCP协议中,我们使用SOCK_STREAM
类型的套接字。另一个常见的套接字类型是SOCK_DGRAM
,表示无连接的、不可靠的数据报文,通常用于UDP协议。
socket(AF_INET, SOCK_STREAM, 0)
:这是一个系统调用,用于创建一个新的套接字。它接受三个参数:地址族(如AF_INET
)、套接字类型(如SOCK_STREAM
)和协议(通常设置为0,让系统自动选择协议,如TCP或UDP)。此函数返回一个套接字文件描述符,用于后续的网络操作。
struct sockaddr_in
:这是一个用于表示IPv4套接字地址的结构体。它包含了地址族、端口号和IPv4地址。在网络编程中,我们需要使用此结构体来设置服务器和客户端的地址信息。
server_addr.sin_family = AF_INET
:设置sockaddr_in
结构体中的地址族字段为AF_INET
,表示使用IPv4协议。
server_addr.sin_port = htons(8080)
:设置sockaddr_in
结构体中的端口号字段。htons()
函数将主机字节序(Host Byte Order)转换为网络字节序(Network Byte Order)。这里我们设置端口号为8080。
INADDR_ANY
:这是一个特殊的IPv4地址(0.0.0.0),表示服务器将监听所有可用的网络接口。当服务器有多个网络接口时,使用INADDR_ANY
可以让服务器接受来自任何接口的连接请求。
server_addr.sin_addr.s_addr = INADDR_ANY
:设置sockaddr_in
结构体中的IPv4地址字段为INADDR_ANY
,表示服务器将监听所有可用的网络接口。
服务器端:
#include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <string.h> #include <iostream> int main() { int server_fd = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = INADDR_ANY; bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); while (true) { char buffer[1024]; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); ssize_t read_len = recvfrom(server_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client_addr, &client_addr_len); buffer[read_len] = '\0'; std::cout << "Received: " << buffer << std::endl; sendto(server_fd, buffer, strlen(buffer), 0, (struct sockaddr *)&client_addr, client_addr_len); } close(server_fd); return 0; }
客户端:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <iostream> int main() { int client_fd = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); const char *message = "Hello, Server!"; sendto(client_fd, message, strlen(message), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); char buffer[1024]; struct sockaddr_in recv_addr; socklen_t recv_addr_len = sizeof(recv_addr); ssize_t read_len = recvfrom(client_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&recv_addr, &recv_addr_len); buffer[read_len] = '\0'; std::cout << "Received: " << buffer << std::endl; close(client_fd); return 0; }
以下是TCP和UDP通信的时序图,展示了客户端与服务器之间的数据流动。
在TCP通信中,我们首先需要建立一个TCP连接,然后才能在这个连接上进行数据传输。以下是TCP通信的详细步骤和时序图:
socket()
函数,创建一个新的套接字。bind()
函数,将套接字绑定到一个指定的地址(包括IP地址和端口号)。listen()
函数,使套接字进入监听模式,等待客户端的连接请求。accept()
函数,阻塞并等待客户端的连接请求。当一个客户端连接请求到来时,accept()
函数返回,并创建一个新的套接字与客户端进行通信。socket()
和connect()
函数,向服务器发起连接请求。connect()
函数会发送一个SYN(同步)数据包到服务器。accept()
函数返回后,回复一个SYN+ACK(确认应答)数据包给客户端。read()
和write()
函数进行数据传输。以下是TCP通信的时序图:
Server Client | | | socket() | | | | bind() | | | | listen() | | | | accept() | | | |--等待客户端连接请求--->| | | | | | socket(), connect() | |<--- SYN ------------| | | |-- SYN + ACK ------->| | | |<--- ACK ------------| | | |<-- Data ------------| | read(), write() | | | |-- Data -----------> | | read(), write() | | |
与TCP不同,UDP是一种无连接的协议,客户端和服务器不需要建立连接就可以直接发送数据。以下是UDP通信的详细步骤:
socket()
函数,创建一个新的套接字。bind()
函数,将套接字绑定到一个指定的地址(包括IP地址和端口号)。socket()
函数,创建一个新的套接字。sendto()
函数发送数据到服务器。recvfrom()
函数接收客户端发送的数据。以下是UDP通信的时序图:
Server Client
| |
| socket() |
| |
| bind() |
| |
|----等待客户端数据---->|
| |
| |
| socket()|
| sendto()|
|<--- Data -----------|
| recvfrom() |
| |
在这种情况下,服务器已经准备好接受客户端的数据。当客户端执行socket()
和sendto()
函数发送数据时,服务器会通过recvfrom()
函数接收这些数据。
在网络通信中,可能会遇到一些异常情况,如TCP握手过程中服务器ACK丢失、第三次握手的ACK丢失等。以下是这些异常情况的处理方式:
当服务器发送的ACK丢失时,客户端将无法收到确认,因此会重新发送SYN。服务器在收到重复的SYN后,会再次发送ACK。这个过程会持续进行,直到客户端收到ACK或达到最大重传次数。
当第三次握手的ACK丢失时,服务器可能仍在等待客户端的ACK。然而,客户端已经认为连接建立,可能会开始发送数据。服务器在收到客户端的数据后,会认为连接已建立,并更新连接状态。因此,即使第三次握手的ACK丢失,TCP连接仍然可以正常建立。
本文详细讨论了Linux网络服务器编程中TCP和UDP两种方式的socket使用、原理分析、代码示例、数据流动时序图,以及一些异常情况的处理方式。理解这些概念和技巧有助于更高效地进行网络服务器编程,应对各种网络通信场景。
TCP与UDP:网络协议的技术原理与要点
从HTTP到QUIC:网络协议的演进与优化
HTTPS:原理、使用方法及安全威胁
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。