Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@JIghtuse
Last active July 23, 2023 09:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JIghtuse/f47c65620796cbba25277b39081340cc to your computer and use it in GitHub Desktop.
Save JIghtuse/f47c65620796cbba25277b39081340cc to your computer and use it in GitHub Desktop.
echo server (select, C++)
#include <array>
#include <cassert>
#include <iostream>
#include <set>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
namespace {
const int kPort = 12345;
const int kReadBufferSize = 1024;
} // namespace
int set_nonblock(int fd)
{
int flags;
#if defined(O_NONBLOCK)
if (-1 == (flags = fcntl(fd, F_GETFL, 0))) {
flags = 0;
}
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
flags = 1;
return ioctl(fd, FIONBIO, &flags);
#endif
}
int maxSocketId(int serverSocket, const std::set<int>& clientSockets)
{
if (clientSockets.empty()) {
return serverSocket;
} else {
return std::max(serverSocket, *clientSockets.rbegin());
}
}
// returns server socket descriptor
int startListening(int port)
{
int serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == -1) {
std::cerr << "cannot open socket\n";
return -1;
}
struct sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(serverSocket, (struct sockaddr *)(&sockAddr), sizeof(sockAddr));
if (ret == -1) {
std::cerr << "failed to bind socket\n";
return -1;
}
ret = set_nonblock(serverSocket);
if (ret == -1) {
std::cerr << "failed to set non-blocking mode\n";
return -1;
}
ret = listen(serverSocket, SOMAXCONN);
if (ret == -1) {
std::cerr << "failed to set non-blocking mode\n";
return -1;
}
return serverSocket;
}
int main()
{
int serverSocket = startListening(kPort);
if (serverSocket == -1) {
std::cerr << "invalid socket\n";
return 1;
}
std::set<int> clientSockets;
fd_set fds;
while (true) {
FD_ZERO(&fds);
FD_SET(serverSocket, &fds);
for (auto sock: clientSockets) {
FD_SET(sock, &fds);
}
int max = maxSocketId(serverSocket, clientSockets);
select(max + 1, &fds, nullptr, nullptr, nullptr);
auto it = clientSockets.begin();
while (it != clientSockets.end()) {
int sock = *it;
if (FD_ISSET(sock, &fds)) {
std::array<char, kReadBufferSize> buf;
int nRead = recv(sock, buf.data(), kReadBufferSize, MSG_NOSIGNAL);
if (nRead <= 0 && errno != EAGAIN) {
shutdown(sock, SHUT_RDWR);
close(sock);
it = clientSockets.erase(it);
} else if (nRead > 0) {
send(sock, buf.data(), nRead, MSG_NOSIGNAL);
++it;
} else {
++it;
}
} else {
++it;
}
}
if (FD_ISSET(serverSocket, &fds)) {
int sock = accept(serverSocket, 0, 0);
if (sock == -1) {
std::cerr << "accept error\n";
return 1;
}
set_nonblock(sock);
clientSockets.insert(sock);
}
}
return 0;
}
@mjy9088
Copy link

mjy9088 commented Apr 28, 2023

in case of #ifndef O_NONBLOCK:

#include <sys/ioctl.h>

return ioctl(fd, FIONBIO, &flags);
  • need include sys/ioctl.h
  • FIOBIO => FIONBIO

@JIghtuse
Copy link
Author

Thanks, @mjy9088! Fixed it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment