Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Poprzednia wersja Nowa wersja | Poprzednia wersja | ||
sk2:sockets_templates [2015/10/14 12:02] jkonczak |
sk2:sockets_templates [2016/11/08 16:19] (aktualna) jkonczak nowe szablony |
||
---|---|---|---|
Linia 1: | Linia 1: | ||
- | ====== Basic ====== | + | ====== 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> | ||
- | <code cpp basic.cpp> | + | ssize_t readData(int fd, char * buffer, ssize_t buffsize){ |
- | #include <iostream> // ____ _ _ _ __ __ | + | auto ret = read(fd, buffer, buffsize); |
- | #include <sys/socket.h> // | _ \ ___ __ _ __| | ___ ___ ___| | _____| |_ / / __ ___ _____ _\ \. | + | if(ret==-1) error(1,errno, "read failed on descriptor %d", fd); |
- | #include <netinet/in.h> // | |_) / _ \/ _` |/ _` | / __|/ _ \ / __| |/ / _ \ __| | | '__/ _ \/ __\ \ / /| | | + | return ret; |
- | #include <arpa/inet.h> // | _ < __/ (_| | (_| | \__ \ (_) | (__| < __/ |_ | | | | __/ (__ \ V / | | | + | } |
- | #include <unistd.h> // |_| \_\___|\__,_|\__,_| |___/\___/ \___|_|\_\___|\__| | |_| \___|\___| \_/ | | | + | |
- | // \_\ /_/ | + | void writeData(int fd, char * buffer, ssize_t count){ |
- | int main(int argc, char **argv) { | + | 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"); | ||
- | int fd = socket(PF_INET, SOCK_STREAM, 0); | + | // 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"); | ||
- | sockaddr_in address { .sin_family = AF_INET, | + | // create socket |
- | .sin_port = htons(13), | + | int sock = socket(resolved->ai_family, resolved->ai_socktype, 0); |
- | .sin_addr = {.s_addr = inet_addr("127.0.0.1") } | + | if(sock == -1) error(1, errno, "socket failed"); |
- | }; | + | |
- | connect(fd, (sockaddr*) &address, sizeof(address)); | + | // attept to connect |
+ | res = connect(sock, resolved->ai_addr, resolved->ai_addrlen); | ||
+ | if(res) error(1, errno, "connect failed"); | ||
- | char buffer[255]; | + | // free memory |
+ | freeaddrinfo(resolved); | ||
- | ssize_t readSize = recv(fd, buffer, 254, 0); | + | /****************************/ |
- | buffer[readSize] = 0; | + | // read from socket, write to stdout |
+ | ssize_t bufsize1 = 255, received1; | ||
+ | char buffer1[bufsize1]; | ||
+ | received1 = readData(sock, buffer1, bufsize1); | ||
+ | writeData(1, buffer1, received1); | ||
- | close(fd); | + | /****************************/ |
- | std::cout << buffer << std::endl; | + | // 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; | return 0; | ||
Linia 33: | Linia 67: | ||
</code> | </code> | ||
- | ====== Klient TCP ====== | + | ====== 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> |