This example demonstrates how to create a basic TCP server application using Windows Sockets API (Winsock). This server will listen for incoming connections on a specified port, accept connections, receive data from clients, and send a simple response back.
Below is the C++ code for a simple TCP server. You can compile and run this code on a Windows machine.
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <vector>
#pragma comment(lib, "Ws2_32.lib")
const int DEFAULT_PORT = 27015;
const int BUFFER_SIZE = 512;
int main() {
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::cerr << "WSAStartup failed: " << iResult << std::endl;
return 1;
}
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
addrinfo hints, *result = NULL;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
iResult = getaddrinfo(NULL, std::to_string(DEFAULT_PORT).c_str(), &hints, &result);
if (iResult != 0) {
std::cerr << "getaddrinfo failed: " << iResult << std::endl;
WSACleanup();
return 1;
}
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl;
freeaddrinfo(result);
WSACleanup();
return 1;
}
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
std::cerr << "bind failed with error: " << WSAGetLastError() << std::endl;
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "listen failed with error: " << WSAGetLastError() << std::endl;
closesocket(ListenSocket);
WSACleanup();
return 1;
}
std::cout << "TCP Server listening on port " << DEFAULT_PORT << "..." << std::endl;
while (true) {
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
std::cerr << "accept failed: " << WSAGetLastError() << std::endl;
continue;
}
std::cout << "Client connected." << std::endl;
char recvbuf[BUFFER_SIZE];
int bytesReceived;
bytesReceived = recv(ClientSocket, recvbuf, BUFFER_SIZE, 0);
if (bytesReceived > 0) {
std::cout << "Bytes received: " << bytesReceived << std::endl;
std::cout << "Message: " << std::string(recvbuf, bytesReceived) << std::endl;
const char* sendbuf = "Hello from TCP Server!";
iResult = send(ClientSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
std::cerr << "send failed: " << WSAGetLastError() << std::endl;
}
std::cout << "Sent response to client." << std::endl;
} else if (bytesReceived == 0) {
std::cout << "Client disconnected." << std::endl;
} else {
std::cerr << "recv failed: " << WSAGetLastError() << std::endl;
}
closesocket(ClientSocket);
std::cout << "Client connection closed." << std::endl;
}
closesocket(ListenSocket);
WSACleanup();
return 0;
}
The code starts by initializing the Winsock library using WSAStartup
. This is crucial for any Winsock application.
A listening socket is created using the socket
function. We specify:
AF_INET
: For IPv4 addresses.SOCK_STREAM
: For a reliable, connection-oriented protocol (TCP).IPPROTO_TCP
: The TCP protocol.The bind
function associates the created socket with a specific IP address and port number. Here, we use NULL
for the IP address (meaning the server will bind to all available interfaces) and DEFAULT_PORT
.
The listen
function puts the socket into a listening state, ready to accept incoming connection requests. SOMAXCONN
indicates the maximum number of pending connections that can be queued.
The accept
function blocks the server's execution until a client attempts to connect. When a connection is established, it returns a new socket descriptor (ClientSocket
) representing the connection to that specific client. The original ListenSocket
continues to listen for new connections.
Once a client is connected:
recv
is used to receive data from the client into a buffer.send
is used to send data back to the client.After communication is finished, closesocket
is used to close the client connection socket. Finally, WSACleanup
is called to de-initialize the Winsock library when the server is shutting down.
Ws2_32.lib
. In Visual Studio, this can be done by going to Project -> Properties -> Linker -> Input -> Additional Dependencies and adding Ws2_32.lib
.You can test this server using a TCP client utility like:
telnet
(if installed) from another command prompt: telnet localhost 27015
When you connect with a client and send some text, the server should print the received message to its console and send back "Hello from TCP Server!".
import socket
HOST = 'localhost'
PORT = 27015
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
message = b'Hello TCP Server!'
s.sendall(message)
data = s.recv(1024)
print(f"Received: {data.decode()}")