我尝试使用 TCP 套接字在终端上与我的朋友进行通信。但我失败了。我想我可以连接服务器,但它不能。当我运行程序时,我给出了IP地址和端口信息,然后我等待了很长时间才输入消息,然后我输入了。之后服务器端就没有任何东西了。
服务器.cpp
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string>
using namespace std;
int main(int argc, char* argv[]){
if (argc != 3){
cout << "Usage: ./client <CLIENT_IP_ADDRESS> <PORT>\n";
return -1;
}
char* client_ip_address = argv[1];
int port = stoi(argv[2]);
// Create a socket for server
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) { // If the socket creation is unsuccessful, it returns -1
cout << "Socket creation has failed.\n";
return -1;
}
// Address and Port informations
sockaddr_in serverAddress; //sockaddr_in: It is the data type that is used to store the address of the socket.
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port); //This function is used to convert the unsigned int from machine byte order to network byte order.
serverAddress.sin_addr.s_addr = inet_addr(client_ip_address);
// Socket binding
bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
if (listen(serverSocket, 10) == -1) {
cout << "Connection listening error: " <<endl;
close(serverSocket);
return -1;
}
std::cout << "The server has been started. Listening for connections...\n";
sockaddr_in clientAddr;
socklen_t clientSize = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientSize);
while (true) {
char messageBuffer[1024] = {0};
recv(clientSocket, messageBuffer, sizeof(messageBuffer), 0);
cout << "Message from client: " << messageBuffer << endl;
if (strcmp(messageBuffer, "Fin") == 0) {
close(serverSocket);
break;
}
// Sending a message to the client.
char buffer[1024] = {0};
cout << "Enter a message: ";
cin.getline(buffer, sizeof(buffer));
send(clientSocket, buffer, strlen(buffer), 0);
if (strcmp(buffer, "Fin") == 0) {
close(serverSocket);
break;
}
}
std::cout << "(Fin) message received. Server is shutting down...\n";
return 0;
}
客户端.cpp
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string>
using namespace std;
int main(int argc, char* argv[]){
if (argc != 3){
cout << "Usage: ./client <SERVER_IP_ADRES> <PORT>\n";
return -1;
}
char* server_ip_address = argv[1];
int port = stoi(argv[2]);
// Create a socket for client
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
cout << "Socket creation has failed.\n";
return -1;
}
// Address and Port informations
sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = inet_addr(server_ip_address);
// Connecting server
connect(clientSocket, (struct sockaddr*)&server_address, sizeof(server_address));
while (true) {
// Kullanıcıdan bir mesaj al
char messageBuffer[1024] = {0};
std::cout << "Enter a message: ";
cin.getline(messageBuffer, sizeof(messageBuffer));
// Sending a message to the server.
send(clientSocket, messageBuffer, strlen(messageBuffer), 0);
if (string(messageBuffer)== "Fin"){
close(clientSocket);
break;
}
char buffer[1024] = {0};
ssize_t bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
cout << "Message from server: " << string(buffer, bytesReceived) << std::endl;
if (string(buffer)== "Fin"){
close(clientSocket);
break;
}
}
std::cout << "(Fin) message received. Client is shutting down...\n";
return 0;
}
我轮流等待与朋友沟通。我在自己的本地实现了这一点。我在终端中打开了一个新选项卡,然后从那里运行并测试了服务器和客户端,它工作没有任何问题。我不能和来自另一个城市的朋友这样做。
由于您的朋友在另一个城市,他们可能位于不同的网络上(除非您在两台计算机之间设置私人 VPN)。
与不同网络上的计算机通信时,您需要在代码之外执行其他步骤:
您需要确保服务器端的 ISP(互联网服务提供商)没有阻止传入连接。许多 ISP 不允许客户在其网络上运行服务器,除非他们为该功能支付额外费用。
如果服务器计算机在 NAT 路由器后面运行(这对于现代 Wifi 设置来说是典型的),则路由器必须配置端口转发规则,以便将 WAN 端的传入连接路由到 LAN 上的服务器计算机边。然后,客户端可以连接到路由器的公共 WAN IP/端口,并被路由到服务器应用程序正在侦听的 LAN IP/端口。
至于你的代码本身,有几个问题。
在服务器中:
确保
bind()
您的服务器套接字连接到直接连接到客户端所在的路由器/互联网的本地 LAN IP。或者您也可以使用 INADDR_ANY
(0.0.0.0
) 绑定到本地计算机上的所有网络接口。正如您的代码所示,请勿绑定到客户端的 IP。
您没有对
bind()
、accept()
、recv()
、send()
或 cin.getline()
进行任何错误检查。
recv()
不会返回以 null 结尾的字符串,并且您不会手动终止 messageBuffer
。您的客户端使用 recv()
的返回值来了解实际收到了多少字节。服务器也需要做同样的事情。
调用
send()
时,您不会发送任何类型的消息长度或消息终止符来让客户端知道消息何时完成发送。对于 TCP,send()
和 recv()
之间不存在 1:1 关系,这两个函数报告的字节数少于请求的字节数,因此您必须循环调用这两个函数以确保发送/接收所有预期数据。
在客户端:
您没有对
connect()
、recv()
、send()
或 cin.getline()
进行任何错误检查。
调用
send()
和 recv()
时,您没有正确处理消息终止,与服务器相同。
尝试更多类似这样的事情:
服务器.cpp
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string>
#include <cstdint>
using namespace std;
bool readRaw(int sckt, void* buffer, size_t bufsize) {
char *ptr = static_cast<char*>(buffer);
while (bufsize > 0) {
ssize_t numBytes = recv(sckt, ptr, bufsize, 0);
if (numBytes < 0) {
cout << "Read error: " << errno << '\n';
return false;
}
if (numBytes == 0) {
cout << "Client disconnected\n";
return false;
}
ptr += numBytes;
bufsize -= numBytes;
}
return true;
}
bool sendRaw(int sckt, const void* buffer, size_t bufsize) {
const char *ptr = static_cast<const char*>(buffer);
while (bufsize > 0) {
ssize_t numBytes = send(sckt, ptr, bufsize, 0);
if (numBytes < 0) {
cout << "Send error: " << errno << '\n';
return false;
}
ptr += numBytes;
bufsize -= numBytes;
}
return true;
}
bool readUint32(int sckt, uint32_t &value) {
if (!readRaw(sckt, &value, sizeof(value))) return false;
value = ntohl(value);
return true;
}
bool sendUint32(int sckt, uint32_t value) {
value = htonl(value);
return sendRaw(sckt, &value, sizeof(value)));
}
bool readString(int sckt, string &message) {
message.clear();
uint32_t len;
if (!readUint32(sckt, len)) return false;
message.resize(len);
return readRaw(sckt, message.data(), len);
}
bool sendString(int sckt, const string &message) {
uint32_t len = message.size();
if (!sendUint32(len)) return false;
return sendRaw(sckt, message.c_str(), len);
}
int main(int argc, char* argv[]) {
if (argc != 3) {
cout << "Usage: ./server <LISTEN_IP_ADDRESS> <PORT>\n";
return -1;
}
char* listen_ip_address = argv[1];
int port = stoi(argv[2]);
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
cout << "Socket creation error: " << errno << '\n';
return -1;
}
sockaddr_in serverAddress; //sockaddr_in: It is the data type that is used to store the address of the socket.
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port); //This function is used to convert the unsigned int from machine byte order to network byte order.
serverAddress.sin_addr.s_addr = inet_addr(listen_ip_address);
if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {
cout << "Socket bind error: " << errno << '\n';
close(serverSocket);
return -1;
}
if (listen(serverSocket, 1) == -1) {
cout << "Socket listen error: " << errno << '\n';
close(serverSocket);
return -1;
}
cout << "The server has been started. Listening for connection...\n";
sockaddr_in clientAddr;
socklen_t clientSize = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientSize);
if (clientSocket < 0) {
cout << "Connection accept error: " << errno << '\n';
close(serverSocket);
return -1;
}
cout << "Client connected\n";
while (true) {
std::string message;
if (!readString(clientSocket, message))
break;
cout << "Message from client: " << message << '\n';
if (message == "Fin")
break;
message.clear();
cout << "Enter a message: ";
if (!getline(cin, message))
break;
if (!sendString(clientSocket, message))
break;
if (message == "Fin")
break;
}
cout << "Server is shutting down...\n";
close(serverSocket);
return 0;
}
客户端.cpp
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string>
#include <cstdint>
using namespace std;
bool readRaw(int sckt, void* buffer, size_t bufsize) {
char *ptr = static_cast<char*>(buffer);
while (bufsize > 0) {
ssize_t numBytes = recv(sckt, ptr, bufsize, 0);
if (numBytes < 0) {
cout << "Read error: " << errno << '\n';
return false;
}
if (numBytes == 0) {
cout << "Server disconnected\n";
return false;
}
ptr += numBytes;
bufsize -= numBytes;
}
return true;
}
bool sendRaw(int sckt, const void* buffer, size_t bufsize) {
const char *ptr = static_cast<const char*>(buffer);
while (bufsize > 0) {
ssize_t numBytes = send(sckt, ptr, bufsize, 0);
if (numBytes < 0) {
cout << "Send error: " << errno << '\n';
return false;
}
ptr += numBytes;
bufsize -= numBytes;
}
return true;
}
bool readUint32(int sckt, uint32_t &value) {
if (!readRaw(sckt, &value, sizeof(value))) return false;
value = ntohl(value);
return true;
}
bool sendUint32(int sckt, uint32_t value) {
value = htonl(value);
return sendRaw(sckt, &value, sizeof(value)));
}
bool readString(int sckt, string &message) {
message.clear();
uint32_t len;
if (!readUint32(sckt, len)) return false;
message.resize(len);
return readRaw(sckt, message.data(), len);
}
bool sendString(int sckt, const string &message) {
uint32_t len = message.size();
if (!sendUint32(len)) return false;
return sendRaw(sckt, message.c_str(), len);
}
int main(int argc, char* argv[]){
if (argc != 3){
cout << "Usage: ./client <SERVER_IP_ADRES> <PORT>\n";
return -1;
}
char* server_ip_address = argv[1];
int port = stoi(argv[2]);
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket < 0) {
cout << "Socket creation error: " << errno << '\n';
return -1;
}
sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = inet_addr(server_ip_address);
if (connect(clientSocket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) {
cout << "Socket connect error: " << errno << '\n';
close(clientSocket);
return -1;
}
while (true) {
cout << "Enter a message: ";
string message;
if (!getline(cin, message))
break;
if (!sendString(clientSocket, message))
break;
if (message == "Fin")
break;
if (!readString(clientSocket, message))
break;
cout << "Message from server: " << meessage << '\n';
if (message == "Fin")
break;
}
cout << "Client is shutting down...\n";
close(clientSocket);
return 0;
}