asyncserver.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. ** selectserver.c -- a cheezy multiperson chat server
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. #include <sys/types.h>
  9. #include <sys/socket.h>
  10. #include <netinet/in.h>
  11. #include <arpa/inet.h>
  12. #include <netdb.h>
  13. #define PORT "9034" // port we're listening on
  14. // get sockaddr, IPv4 or IPv6:
  15. void *get_in_addr(struct sockaddr *sa)
  16. {
  17. if (sa->sa_family == AF_INET) {
  18. return &(((struct sockaddr_in*)sa)->sin_addr);
  19. }
  20. return &(((struct sockaddr_in6*)sa)->sin6_addr);
  21. }
  22. int main(void){
  23. fd_set master; // master file descriptor list
  24. fd_set read_fds; // temp file descriptor list for select()
  25. int fdmax; // maximum file descriptor number
  26. int listener; // listening socket descriptor
  27. int newfd; // newly accept()ed socket descriptor
  28. struct sockaddr_storage remoteaddr; // client address
  29. socklen_t addrlen;
  30. char buf[256]; // buffer for client data
  31. int nbytes;
  32. char remoteIP[INET6_ADDRSTRLEN];
  33. int yes=1; // for setsockopt() SO_REUSEADDR, below
  34. int i, j, rv;
  35. struct addrinfo hints, *ai, *p;
  36. FD_ZERO(&master); // clear the master and temp sets
  37. FD_ZERO(&read_fds);
  38. // get us a socket and bind it
  39. memset(&hints, 0, sizeof hints);
  40. hints.ai_family = AF_UNSPEC;
  41. hints.ai_socktype = SOCK_STREAM;
  42. hints.ai_flags = AI_PASSIVE;
  43. if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
  44. fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
  45. exit(1);
  46. }
  47. for(p = ai; p != NULL; p = p->ai_next) {
  48. listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
  49. if (listener < 0) {
  50. continue;
  51. }
  52. // lose the pesky "address already in use" error message
  53. setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
  54. if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
  55. close(listener);
  56. continue;
  57. }
  58. break;
  59. }
  60. // if we got here, it means we didn't get bound
  61. if (p == NULL) {
  62. fprintf(stderr, "selectserver: failed to bind\n");
  63. exit(2);
  64. }
  65. freeaddrinfo(ai); // all done with this
  66. // listen
  67. if (listen(listener, 10) == -1) {
  68. perror("listen");
  69. exit(3);
  70. }
  71. // add the listener to the master set
  72. FD_SET(listener, &master);
  73. // keep track of the biggest file descriptor
  74. fdmax = listener; // so far, it's this one
  75. // main loop
  76. for(;;) {
  77. read_fds = master; // copy it
  78. if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
  79. perror("select");
  80. exit(4);
  81. }
  82. // run through the existing connections looking for data to read
  83. for(i = 0; i <= fdmax; i++) {
  84. if (FD_ISSET(i, &read_fds)) { // we got one!!
  85. if (i == listener) {
  86. // handle new connections
  87. addrlen = sizeof remoteaddr;
  88. newfd = accept(listener,
  89. (struct sockaddr *)&remoteaddr,
  90. &addrlen);
  91. if (newfd == -1) {
  92. perror("accept");
  93. } else {
  94. FD_SET(newfd, &master); // add to master set
  95. if (newfd > fdmax) { // keep track of the max
  96. fdmax = newfd;
  97. }
  98. printf("selectserver: new connection from %s on "
  99. "socket %d\n",
  100. inet_ntop(remoteaddr.ss_family,
  101. get_in_addr((struct sockaddr*)&remoteaddr),
  102. remoteIP, INET6_ADDRSTRLEN),
  103. newfd);
  104. }
  105. } else {
  106. // handle data from a client
  107. memset(buf, 0, 256);
  108. if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
  109. // got error or connection closed by client
  110. if (nbytes == 0) {
  111. // connection closed
  112. printf("selectserver: socket %d hung up\n", i);
  113. } else {
  114. perror("recv");
  115. }
  116. close(i); // bye!
  117. FD_CLR(i, &master); // remove from master set
  118. } else {
  119. // we got some data from a client
  120. for(j = 0; j <= fdmax; j++) {
  121. // send to everyone!
  122. if (FD_ISSET(j, &master)) {
  123. // except the listener and ourselves
  124. if (j != listener && j != i) {
  125. if (send(j, buf, nbytes, 0) == -1) {
  126. perror("send");
  127. }
  128. }
  129. }
  130. }
  131. }
  132. } // END handle data from client
  133. } // END got new incoming connection
  134. } // END looping through file descriptors
  135. } // END for(;;)--and you thought it would never end!
  136. return 0;
  137. }