/* * File: TCPEchoServer-Thread.c 编译参数加上 -lpthread * Author: 云守护 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <pthread.h> #include "Utility.h" //线程函数 void *ThreadMain(void *arg); struct ThreadArgs { int client_sock; }; int main(int argc, char** argv) { if (argc != 2) DieWithUserMessage("param", "<server port/Service>"); char *service = argv[1]; int server_sock = SetupTCPServerSocket(service); if (server_sock < 0) DieWithUserMessage("SetupTCPServerSocket() failed!", "unable to establish"); for (;;) { int client_sock = AcceptTCPConnection(server_sock); struct ThreadArgs* thread_args = (struct ThreadArgs*) malloc(sizeof (struct ThreadArgs)); if (thread_args == NULL) { DieWithSystemMessage("malloc() failed!"); } thread_args->client_sock = client_sock; pthread_t tid; int ret = pthread_create(&tid, NULL, ThreadMain, thread_args); if (ret != 0) DieWithUserMessage("pthread_create() failed!", strerror(ret)); printf("with thread %lu \n", (unsigned long int)tid); } return (EXIT_SUCCESS); } void *ThreadMain(void* arg) { pthread_detach(pthread_self()); int client_sock = ((struct ThreadArgs*) arg)->client_sock; free(arg); HandleTCPClient(client_sock); return (NULL); }
/* * File: Utility.h * Author: 云守护 [email protected] */ #ifndef UTILITY_H #define UTILITY_H #ifdef __cplusplus extern "C" { #endif #include <stdbool.h> #include <stdio.h> #include <sys/socket.h> // Handle error with user msg void DieWithUserMessage(const char *msg, const char *detail); // Handle error with sys msg void DieWithSystemMessage(const char *msg); // Print socket address void PrintSocketAddress(const struct sockaddr *address, FILE *stream); // Test socket address equality bool SockAddrsEqual(const struct sockaddr *addr1, const struct sockaddr *addr2); // Create, bind, and listen a new TCP server socket int SetupTCPServerSocket(const char *service); // Accept a new TCP connection on a server socket int AcceptTCPConnection(int servSock); // Handle new TCP client void HandleTCPClient(int clntSocket); // Create and connect a new TCP client socket int SetupTCPClientSocket(const char *server, const char *service); enum sizeConstants { MAXSTRINGLENGTH = 128, BUFSIZE = 512, }; #ifdef __cplusplus } #endif #endif /* UTILITY_H */
/* * File: Utility.c * Author: 云守护 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include "Utility.h" static const int MAXPENDING = 5; // Maximum outstanding connection requests void DieWithUserMessage(const char *msg, const char *detail) { fputs(msg, stderr); fputs(": ", stderr); fputs(detail, stderr); fputc('\n', stderr); exit(1); } void DieWithSystemMessage(const char *msg) { perror(msg); exit(1); } void PrintSocketAddress(const struct sockaddr *address, FILE *stream) { // Test for address and stream if (address == NULL || stream == NULL) return; void *numericAddress; // Pointer to binary address // Buffer to contain result (IPv6 sufficient to hold IPv4) char addrBuffer[INET6_ADDRSTRLEN]; in_port_t port; // Port to print // Set pointer to address based on address family switch (address->sa_family) { case AF_INET: numericAddress = &((struct sockaddr_in *) address)->sin_addr; port = ntohs(((struct sockaddr_in *) address)->sin_port); break; case AF_INET6: numericAddress = &((struct sockaddr_in6 *) address)->sin6_addr; port = ntohs(((struct sockaddr_in6 *) address)->sin6_port); break; default: fputs("[unknown type]", stream); // Unhandled type return; } // Convert binary to printable address if (inet_ntop(address->sa_family, numericAddress, addrBuffer, sizeof(addrBuffer)) == NULL) fputs("[invalid address]", stream); // Unable to convert else { fprintf(stream, "%s", addrBuffer); if (port != 0) // Zero not valid in any socket addr fprintf(stream, "-%u", port); } } bool SockAddrsEqual(const struct sockaddr *addr1, const struct sockaddr *addr2) { if (addr1 == NULL || addr2 == NULL) return addr1 == addr2; else if (addr1->sa_family != addr2->sa_family) return false; else if (addr1->sa_family == AF_INET) { struct sockaddr_in *ipv4Addr1 = (struct sockaddr_in *) addr1; struct sockaddr_in *ipv4Addr2 = (struct sockaddr_in *) addr2; return ipv4Addr1->sin_addr.s_addr == ipv4Addr2->sin_addr.s_addr && ipv4Addr1->sin_port == ipv4Addr2->sin_port; } else if (addr1->sa_family == AF_INET6) { struct sockaddr_in6 *ipv6Addr1 = (struct sockaddr_in6 *) addr1; struct sockaddr_in6 *ipv6Addr2 = (struct sockaddr_in6 *) addr2; return memcmp(&ipv6Addr1->sin6_addr, &ipv6Addr2->sin6_addr, sizeof(struct in6_addr)) == 0 && ipv6Addr1->sin6_port == ipv6Addr2->sin6_port; } else return false; } int SetupTCPClientSocket(const char *host, const char *service) { // Tell the system what kind(s) of address info we want struct addrinfo addrCriteria; // Criteria for address match memset(&addrCriteria, 0, sizeof(addrCriteria)); // Zero out structure addrCriteria.ai_family = AF_UNSPEC; // v4 or v6 is OK addrCriteria.ai_socktype = SOCK_STREAM; // Only streaming sockets addrCriteria.ai_protocol = IPPROTO_TCP; // Only TCP protocol // Get address(es) struct addrinfo *servAddr; // Holder for returned list of server addrs int rtnVal = getaddrinfo(host, service, &addrCriteria, &servAddr); if (rtnVal != 0) DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal)); int sock = -1; struct addrinfo *addr = servAddr; while ( addr != NULL) { // Create a reliable, stream socket using TCP sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (sock < 0) continue; // Socket creation failed; try next address // Establish the connection to the echo server if (connect(sock, addr->ai_addr, addr->ai_addrlen) == 0) break; // Socket connection succeeded; break and return socket close(sock); // Socket connection failed; try next address sock = -1; addr = addr->ai_next; } freeaddrinfo(servAddr); // Free addrinfo allocated in getaddrinfo() return sock; } int SetupTCPServerSocket(const char *service) { // Construct the server address structure struct addrinfo addrCriteria; // Criteria for address match memset(&addrCriteria, 0, sizeof(addrCriteria)); // Zero out structure addrCriteria.ai_family = AF_UNSPEC; // Any address family addrCriteria.ai_flags = AI_PASSIVE; // Accept on any address/port addrCriteria.ai_socktype = SOCK_STREAM; // Only stream sockets addrCriteria.ai_protocol = IPPROTO_TCP; // Only TCP protocol struct addrinfo *servAddr; // List of server addresses int rtnVal = getaddrinfo(NULL, service, &addrCriteria, &servAddr); if (rtnVal != 0) DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal)); int servSock = -1; struct addrinfo *addr = servAddr; while ( addr != NULL) { // Create a TCP socket servSock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (servSock < 0) continue; // Socket creation failed; try next address // Bind to the local address and set socket to listen if ((bind(servSock, addr->ai_addr, addr->ai_addrlen) == 0) && (listen(servSock, MAXPENDING) == 0)) { // Print local address of socket struct sockaddr_storage localAddr; socklen_t addrSize = sizeof(localAddr); if (getsockname(servSock, (struct sockaddr *) &localAddr, &addrSize) < 0) DieWithSystemMessage("getsockname() failed"); fputs("Binding to ", stdout); PrintSocketAddress((struct sockaddr *) &localAddr, stdout); fputc('\n', stdout); break; // Bind and listen successful } close(servSock); // Close and try again servSock = -1; addr = addr->ai_next; } // Free address list allocated by getaddrinfo() freeaddrinfo(servAddr); return servSock; } int AcceptTCPConnection(int servSock) { struct sockaddr_storage clntAddr; // Client address // Set length of client address structure (in-out parameter) socklen_t clntAddrLen = sizeof(clntAddr); // Wait for a client to connect int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen); if (clntSock < 0) DieWithSystemMessage("accept() failed"); // clntSock is connected to a client! fputs("Handling client ", stdout); PrintSocketAddress((struct sockaddr *) &clntAddr, stdout); fputc('\n', stdout); return clntSock; } void HandleTCPClient(int clntSocket) { char buffer[BUFSIZE]; // Buffer for echo string memset(buffer,0,sizeof(buffer)); // Receive message from client ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); if (numBytesRcvd < 0) DieWithSystemMessage("recv() failed"); puts(buffer); putc('\n',stdout); // Send received string and receive again until end of stream while (numBytesRcvd > 0) { // 0 indicates end of stream // Echo message back to client ssize_t numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0); if (numBytesSent < 0) DieWithSystemMessage("send() failed"); else if (numBytesSent != numBytesRcvd) DieWithUserMessage("send()", "sent unexpected number of bytes"); // See if there is more data to receive numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); if (numBytesRcvd < 0) DieWithSystemMessage("recv() failed"); } close(clntSocket); // Close client socket }