tcp
TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的通信协议。它是互联网协议套件(TCP/IP协议栈)的一部分,用于在网络上可靠地传输数据。
以下是 TCP 协议的主要特点和介绍:
可靠性: TCP 提供可靠的、面向连接的通信。通过使用确认机制和超时重传机制,确保数据的可靠性传输。如果数据包在传输过程中丢失或损坏,TCP 会负责重新发送丢失的数据。
面向连接: 在进行数据传输之前,TCP 首先在通信的两端建立连接。这个连接是全双工的,双方可以同时进行数据的收发。连接的建立、维护和释放是由 TCP 协议负责的。
流控制: TCP 使用流控制机制来防止发送方发送过多的数据导致接收方无法处理。通过窗口大小的动态调整,TCP 可以在发送和接收之间实现平衡。
拥塞控制: TCP 通过拥塞控制机制来适应网络的变化。它通过监测网络的拥塞状况来调整数据的传输速率,以避免过多的数据导致网络拥塞。
字节流传输: TCP 将数据看作是字节流,而不是分割成独立的消息。这意味着发送的数据可以按照任意大小的块进行划分,而接收方会将它们重新组装成完整的字节流。
面向字节流而非报文: TCP 将数据看作是一连串的字节而非报文。这使得它更加灵活,但也需要应用层协议来确定消息的边界。

tcp服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| #include <iostream> #include <thread> #include <cstring> #include <arpa/inet.h> #include <unistd.h> #include <mutex> #include <chrono> #include <ctime> #include <iomanip> #include <sstream>
std::mutex coutMutex;
std::string GetCurrentTimestamp() { auto now = std::chrono::system_clock::now(); auto time_point = std::chrono::system_clock::to_time_t(now); std::stringstream ss; ss << std::put_time(std::localtime(&time_point), "%Y-%m-%d %H:%M:%S"); return ss.str(); }
void HandleClient(int clientSocket) { char buffer[1024]; std::memset(buffer, 0, sizeof(buffer)); std::thread receiveThread([&]() { while(true) { ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0); if(bytesRead <= 0) { std::cerr << "[" << GetCurrentTimestamp() << "] Error reading from client\n"; break; }else{ std::lock_guard<std::mutex> lock(coutMutex); std::cout << "[" << GetCurrentTimestamp() << "] Received from client: " << buffer << std::endl;
const char* response = "Hello from server!"; ssize_t bytesSent = send(clientSocket, response, std::strlen(response), 0); if(bytesSent <= 0) { std::cerr << "[" << GetCurrentTimestamp() << "] Error sending response to client\n"; break; } } std::memset(buffer, 0, sizeof(buffer)); } });
std::thread sendThread([&]() { while (true) { std::string message; std::cout << "[" << GetCurrentTimestamp() << "] Enter message to send (or 'exit' to quit): "; std::getline(std::cin, message); ssize_t bytesSent = send(clientSocket, message.c_str(), message.length(), 0); if (bytesSent <= 0) { std::cerr << "[" << GetCurrentTimestamp() << "] Error sending message to client\n"; break; }
if (message == "exit") { std::cout << "[" << GetCurrentTimestamp() << "] Exiting...\n"; break; } } }); receiveThread.join(); sendThread.join(); close(clientSocket); }
int main() { int serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error creating socket\n"; return -1; } int reuse = 1; if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error setting socket options\n"; close(serverSocket); return -1; } struct sockaddr_in serverAddress; std::memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = INADDR_ANY; serverAddress.sin_port = htons(8888);
if(bind(serverSocket, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(serverAddress)) == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error binding address\n"; close(serverSocket); return -1; } if(listen(serverSocket, 5) == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error listening for connections\n"; close(serverSocket); return -1; }
std::cout << "[" << GetCurrentTimestamp() << "] Server listening on port 8888\n"; while(true) { struct sockaddr_in clientAddress; socklen_t clientAddressLength = sizeof(clientAddress); int clientSocket = accept(serverSocket, reinterpret_cast<struct sockaddr*>(&clientAddress), &clientAddressLength); if(clientSocket == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error accepting connection\n"; continue; } std::thread(HandleClient, clientSocket).detach(); } close(serverSocket); return 0; }
|
tcp客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| #include <iostream> #include <thread> #include <cstring> #include <arpa/inet.h> #include <unistd.h> #include <mutex> #include <chrono> #include <ctime> #include <iomanip> #include <sstream>
std::mutex coutMutex;
std::string GetCurrentTimestamp() { auto now = std::chrono::system_clock::now(); auto time_point = std::chrono::system_clock::to_time_t(now); std::stringstream ss; ss << std::put_time(std::localtime(&time_point), "%Y-%m-%d %H:%M:%S"); return ss.str(); }
void ReceiveMessages(int clientSocket) { char buffer[1024]; std::memset(buffer, 0, sizeof(buffer));
while(true) { ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0); if(bytesRead <= 0) { std::cerr << "[" << GetCurrentTimestamp() << "] Error reading from server\n"; break; }else{ std::lock_guard<std::mutex> lock(coutMutex); std::cout << "[" << GetCurrentTimestamp() << "] Received from server: " << buffer << std::endl; } std::memset(buffer, 0, sizeof(buffer)); } }
void SendMessages(int clientSocket) { while(true){ std::string message; std::cout << "[" << GetCurrentTimestamp() << "] Enter message to send (or 'exit' to quit): "; std::getline(std::cin, message);
ssize_t bytesSent = send(clientSocket, message.c_str(), message.length(), 0); if(bytesSent <= 0) { std::cerr << "[" << GetCurrentTimestamp() << "] Error sending message to server\n"; break; }
if(message == "exit") { std::cout << "[" << GetCurrentTimestamp() << "] Exiting...\n"; break; } } }
int main() { int clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (clientSocket == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error creating socket\n"; return -1; }
struct sockaddr_in serverAddress; std::memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); serverAddress.sin_port = htons(8888);
if (connect(clientSocket, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(serverAddress)) == -1) { std::cerr << "[" << GetCurrentTimestamp() << "] Error connecting to server\n"; close(clientSocket); return -1; }
std::thread receiveThread(ReceiveMessages, clientSocket);
std::thread sendThread(SendMessages, clientSocket);
receiveThread.join(); sendThread.join();
close(clientSocket);
return 0; }
|
socket函数
套接字初始化,根据需求设置套接字,
1 2 3 4 5 6
| int socket(int __domain, int __type, int __[protocol]) /*__domain 协议族,如IPV4,IPV6 __type 套接字类型 SOCK_STREAM字节流套接字,SOCK_DGRAM数据报套接字,SOCK_RAW原始套接字 __protocol 协议编号 IPPROTO_TCPTCP传输协议,IPPROTO_UDPUDP传输协议 返回新套接字的文件描述符,或-1表示错误,错误可通过errno获得。 */
|
bind函数
函数功能:套接字与端口绑定,即将套接字与地址结构进行绑定,绑定之后,在进行网络程序设计时,套接字所代表IP地址和端口地址及协议类型等参数按绑定值进行操作。
将套接字描述符(FD)与本机网络地址ADDR(长度为LEN字节)和端口号绑定在一起。
1 2 3 4 5 6 7 8 9 10
| extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len) __THROW;
|
connect函数
函数功能:连接指定参数的服务器。客户端建立套接字之后,无需进行地址绑定,直接连接服务器。
在套接字FD上打开一个连接以对等地址ADDR(长度为LEN字节)。对于无连接套接字类型,只需设置发送到的默认地址和接收传输的唯一地址。
1 2 3 4 5 6 7
| extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);
|
listen服务器函数
函数功能:设置服务器侦听连接,初始化服务器可连接队列,由于服务器需满足多个客户端连接请求,而服务器在某时间仅能处理有限个数的客户端连接请求,故服务器需设置队列长度。
1 2 3
| extern int listen (int __fd, int __n) __THROW;
|
accept服务器函数
函数功能:接收客户端连接。当客户端的连接请求到达服务器主机侦听的端口时,此时客户端连接会在队列中等待,直到使用服务器处理接收请求。
1 2 3 4 5 6
| extern int accept (int __fd, __SOCKADDR_ARG __addr,socklen_t *__restrict __addr_len);
|
send()函数与recv()函数
1 2 3 4 5 6
| extern ssize_t send (int __fd, const void *__buf, size_t __n, int __flags); extern ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);
|