Różnice między wybraną wersją a wersją aktualną.
| Nowa wersja | Poprzednia wersja | ||
|
sk2:sockets_templates [2015/10/12 22:50] jkonczak utworzono |
sk2:sockets_templates [2016/11/08 16:19] (aktualna) jkonczak nowe szablony |
||
|---|---|---|---|
| Linia 1: | Linia 1: | ||
| - | ====== Klient TCP ====== | + | ====== Klient TCP odbierz - wyślij ====== |
| + | <code cpp tcp_client_template.cpp> | ||
| + | #include <cstdlib> | ||
| + | #include <unistd.h> | ||
| + | #include <sys/socket.h> | ||
| + | #include <netinet/in.h> | ||
| + | #include <errno.h> | ||
| + | #include <error.h> | ||
| + | #include <netdb.h> | ||
| + | #include <sys/epoll.h> | ||
| + | #include <poll.h> | ||
| + | #include <thread> | ||
| + | |||
| + | ssize_t readData(int fd, char * buffer, ssize_t buffsize){ | ||
| + | auto ret = read(fd, buffer, buffsize); | ||
| + | if(ret==-1) error(1,errno, "read failed on descriptor %d", fd); | ||
| + | return ret; | ||
| + | } | ||
| + | |||
| + | void writeData(int fd, char * buffer, ssize_t count){ | ||
| + | auto ret = write(fd, buffer, count); | ||
| + | if(ret==-1) error(1, errno, "write failed on descriptor %d", fd); | ||
| + | if(ret!=count) error(0, errno, "wrote less than requested to descriptor %d (%ld/%ld)", fd, count, ret); | ||
| + | } | ||
| + | |||
| + | int main(int argc, char ** argv){ | ||
| + | if(argc!=3) error(1,0,"Need 2 args"); | ||
| + | |||
| + | // Resolve arguments to IPv4 address with a port number | ||
| + | addrinfo *resolved, hints={.ai_flags=0, .ai_family=AF_INET, .ai_socktype=SOCK_STREAM}; | ||
| + | int res = getaddrinfo(argv[1], argv[2], &hints, &resolved); | ||
| + | if(res || !resolved) error(1, errno, "getaddrinfo"); | ||
| + | |||
| + | // create socket | ||
| + | int sock = socket(resolved->ai_family, resolved->ai_socktype, 0); | ||
| + | if(sock == -1) error(1, errno, "socket failed"); | ||
| + | |||
| + | // attept to connect | ||
| + | res = connect(sock, resolved->ai_addr, resolved->ai_addrlen); | ||
| + | if(res) error(1, errno, "connect failed"); | ||
| + | |||
| + | // free memory | ||
| + | freeaddrinfo(resolved); | ||
| + | |||
| + | /****************************/ | ||
| + | |||
| + | // read from socket, write to stdout | ||
| + | ssize_t bufsize1 = 255, received1; | ||
| + | char buffer1[bufsize1]; | ||
| + | received1 = readData(sock, buffer1, bufsize1); | ||
| + | writeData(1, buffer1, received1); | ||
| + | |||
| + | /****************************/ | ||
| + | |||
| + | // read from stdin, write to socket | ||
| + | ssize_t bufsize2 = 255, received2; | ||
| + | char buffer2[bufsize2]; | ||
| + | received2 = readData(0, buffer2, bufsize2); | ||
| + | writeData(sock, buffer2, received2); | ||
| + | |||
| + | /****************************/ | ||
| + | |||
| + | close(sock); | ||
| + | |||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ====== Serwer TCP połącz - wyślij do wszystkich ====== | ||
| + | <code cpp tcp_server_template.cpp> | ||
| + | #include <cstdlib> | ||
| + | #include <cstdio> | ||
| + | #include <unistd.h> | ||
| + | #include <sys/socket.h> | ||
| + | #include <netinet/in.h> | ||
| + | #include <arpa/inet.h> | ||
| + | #include <errno.h> | ||
| + | #include <error.h> | ||
| + | #include <netdb.h> | ||
| + | #include <sys/epoll.h> | ||
| + | #include <poll.h> | ||
| + | #include <thread> | ||
| + | #include <unordered_set> | ||
| + | #include <signal.h> | ||
| + | |||
| + | // server socket | ||
| + | int servFd; | ||
| + | |||
| + | // client sockets | ||
| + | std::unordered_set<int> clientFds; | ||
| + | |||
| + | // handles SIGINT | ||
| + | void ctrl_c(int); | ||
| + | |||
| + | // sends data to clientFds excluding fd | ||
| + | void sendToAllBut(int fd, char * buffer, int count); | ||
| + | |||
| + | // converts cstring to port | ||
| + | uint16_t readPort(char * txt); | ||
| + | |||
| + | // sets SO_REUSEADDR | ||
| + | void setReuseAddr(int sock); | ||
| + | |||
| + | int main(int argc, char ** argv){ | ||
| + | // get and validate port number | ||
| + | if(argc != 2) error(1, 0, "Need 1 arg (port)"); | ||
| + | auto port = readPort(argv[1]); | ||
| + | |||
| + | // create socket | ||
| + | servFd = socket(AF_INET, SOCK_STREAM, 0); | ||
| + | if(servFd == -1) error(1, errno, "socket failed"); | ||
| + | |||
| + | // graceful ctrl+c exit | ||
| + | signal(SIGINT, ctrl_c); | ||
| + | // prevent dead sockets from throwing pipe errors on write | ||
| + | signal(SIGPIPE, SIG_IGN); | ||
| + | |||
| + | setReuseAddr(servFd); | ||
| + | |||
| + | // bind to any address and port provided in arguments | ||
| + | sockaddr_in serverAddr{.sin_family=AF_INET, .sin_port=htons((short)port), .sin_addr={INADDR_ANY}}; | ||
| + | int res = bind(servFd, (sockaddr*) &serverAddr, sizeof(serverAddr)); | ||
| + | if(res) error(1, errno, "bind failed"); | ||
| + | |||
| + | // enter listening mode | ||
| + | res = listen(servFd, 1); | ||
| + | if(res) error(1, errno, "listen failed"); | ||
| + | |||
| + | /****************************/ | ||
| + | |||
| + | while(true){ | ||
| + | // prepare placeholders for client address | ||
| + | sockaddr_in clientAddr{0}; | ||
| + | socklen_t clientAddrSize = sizeof(clientAddr); | ||
| + | |||
| + | // accept new connection | ||
| + | auto clientFd = accept(servFd, (sockaddr*) &clientAddr, &clientAddrSize); | ||
| + | if(clientFd == -1) error(1, errno, "accept failed"); | ||
| + | |||
| + | // add client to all clients set | ||
| + | clientFds.insert(clientFd); | ||
| + | |||
| + | // tell who has connected | ||
| + | printf("new connection from: %s:%hu (fd: %d)\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), clientFd); | ||
| + | |||
| + | /****************************/ | ||
| + | |||
| + | // read a message | ||
| + | char buffer[255]; | ||
| + | int count = read(clientFd, buffer, 255); | ||
| + | |||
| + | if(count < 1) { | ||
| + | printf("removing %d\n", clientFd); | ||
| + | clientFds.erase(clientFd); | ||
| + | close(clientFd); | ||
| + | continue; | ||
| + | } else { | ||
| + | // broadcast the message | ||
| + | sendToAllBut(clientFd, buffer, count); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | /****************************/ | ||
| + | } | ||
| + | |||
| + | uint16_t readPort(char * txt){ | ||
| + | char * ptr; | ||
| + | auto port = strtol(txt, &ptr, 10); | ||
| + | if(*ptr!=0 || port<1 || (port>((1<<16)-1))) error(1,0,"illegal argument %s", txt); | ||
| + | return port; | ||
| + | } | ||
| + | |||
| + | void setReuseAddr(int sock){ | ||
| + | const int one = 1; | ||
| + | int res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | ||
| + | if(res) error(1,errno, "setsockopt failed"); | ||
| + | } | ||
| + | |||
| + | void ctrl_c(int){ | ||
| + | for(int clientFd : clientFds) | ||
| + | close(clientFd); | ||
| + | close(servFd); | ||
| + | printf("Closing server\n"); | ||
| + | exit(0); | ||
| + | } | ||
| + | |||
| + | void sendToAllBut(int fd, char * buffer, int count){ | ||
| + | int res; | ||
| + | decltype(clientFds) bad; | ||
| + | for(int clientFd : clientFds){ | ||
| + | if(clientFd == fd) continue; | ||
| + | res = write(clientFd, buffer, count); | ||
| + | if(res!=count) | ||
| + | bad.insert(clientFd); | ||
| + | } | ||
| + | for(int clientFd : bad){ | ||
| + | printf("removing %d\n", clientFd); | ||
| + | clientFds.erase(clientFd); | ||
| + | close(clientFd); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </code> | ||
| + | |||
| + | ====== Nawiązanie połączenia TCP ====== | ||
| <code cpp tcp_client.cpp> | <code cpp tcp_client.cpp> | ||
| #include <sys/socket.h> | #include <sys/socket.h> | ||