天天看点

linux c socket之通用socket,支持ipv4和ipv6

/* 
 * File:   TCPEchoClient.c
 * Author: 云守护  通用客户端
 *
 * Created on 2013年11月13日, 下午3:49
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "DieWithMessage.h"
//建立客户端socket
int SetupTCPClientSocket(const char *host,const char* service)
{
    //配置想要的地址信息
    struct addrinfo addrCriteria;
    memset(&addrCriteria,0,sizeof(addrCriteria));
    addrCriteria.ai_family=AF_UNSPEC;
    addrCriteria.ai_socktype=SOCK_STREAM;
    addrCriteria.ai_protocol=IPPROTO_TCP;
    
    struct addrinfo *server_addr;
    //获取地址信息
    int retVal=getaddrinfo(host,service,&addrCriteria,&server_addr);
    if(retVal!=0)
        DieWithUserMessage("getaddrinfo() failed!",gai_strerror(retVal));
    int sock=-1;
    struct addrinfo *addr=server_addr;
    while(addr!=NULL)
    {
        //建立socket
        sock=socket(addr->ai_family,addr->ai_socktype,addr->ai_protocol);
        if(sock<0)
            continue;
        if(connect(sock,addr->ai_addr,addr->ai_addrlen)==0)
        {
            //链接成功,就中断循环
            break;
        }
        //没有链接成功,就继续尝试下一个
        close(sock);
        sock=-1;
        addr=addr->ai_next;
    }
    freeaddrinfo(server_addr);
    return sock;
}

int main(int argc, char** argv) {
    if(argc<3||argc>4)
    {
        DieWithUserMessage("param","<server addree/Name> <Echo Word> [<Server Port/Service>]");             
    }
    char *server=argv[1];
    char* echoString=argv[2];
    char * service=(argc==4)?argv[3]:"echo";
    //建立链接
    int sock=SetupTCPClientSocket(server,service);
    if(sock<0)
        DieWithUserMessage("SetupTCPClientSocket failed!","Unable to connect");
    size_t echoStringLen=strlen(echoString);
    //发送数据
    ssize_t numBytes=send(sock,echoString,echoStringLen,0);
    if(numBytes<0)
        DieWithSystemMessage("send() failed!");
    else if(numBytes!=echoStringLen)
    {
        DieWithUserMessage("send() ","sent unexpected number of bytes");      
    }
    unsigned int totalBytesRcvd=0;
    fputs("Received:",stdout);
    while(totalBytesRcvd<echoStringLen)
    {
        char buffer[BUFSIZ];
         //接收数据
        numBytes=recv(sock,buffer,BUFSIZ-1,0);
        if(numBytes<0)
            DieWithSystemMessage("recv() failed!");
        else if(numBytes==0)
        {
            DieWithUserMessage("recv()","connection closed prematurely");         
        }
        totalBytesRcvd+=numBytes;
        buffer[numBytes]='\0';
        //打印到控制台
        fputs(buffer,stdout);
        
    }
    fputc('\n',stdout);
    close(sock);  
    return (EXIT_SUCCESS);
}

           
/* 
 * File:   TCPEchoServer.c 通用服务端
 * Author: 云守护
 * Created on 2013年11月14日, 上午10:10
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include "DieWithMessage.h"

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);
  }
}


static const int MAXPENDING=5;

int SetupTCPServerSocket(const char *service)
{
    //配置地址信息 
    struct addrinfo addrCriteria;
    memset(&addrCriteria,0,sizeof(addrCriteria));
    addrCriteria.ai_family=AF_UNSPEC;
    addrCriteria.ai_flags=AI_PASSIVE;
    addrCriteria.ai_socktype=SOCK_STREAM;
    addrCriteria.ai_protocol=IPPROTO_TCP;
    
    struct addrinfo *server_addr;
    //获取地址信息
    int retVal=getaddrinfo(NULL,service ,&addrCriteria,&server_addr);
    if(retVal!=0)
        DieWithUserMessage("getaddrinfo failed!",gai_strerror(retVal));
    int server_sock=-1;
    struct addrinfo *addr=server_addr;
    while(addr!=NULL)
    {
        //建立socket
        server_sock=socket(server_addr->ai_family,server_addr->ai_socktype,server_addr->ai_protocol);
        if(server_sock<0)
            continue;
        //绑定端口和监听端口
        if((bind(server_sock,server_addr->ai_addr,server_addr->ai_addrlen)==0)&& listen(server_sock,MAXPENDING)==0)
        {
            struct sockaddr_storage local_addr;
            socklen_t addr_size=sizeof(local_addr);
            if(getsockname(server_sock,(struct sockaddr *)&local_addr,&addr_size)<0)
            {
                DieWithSystemMessage("getsockname() failed!");
            }
            fputs("Binding to ",stdout);
            PrintSocketAddress((struct sockaddr*)&local_addr,stdout);
            fputc('\n',stdout);
            break;       
        }
        close(server_sock);
        server_sock=-1;
        addr=addr->ai_next;
    }
    freeaddrinfo(server_addr);
    return server_sock;  
}

void HandleTCPClient(int clntSocket) {
  char buffer[BUFSIZ]; // Buffer for echo string
  // Receive message from client
  ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZ, 0);
  if (numBytesRcvd < 0)
    DieWithSystemMessage("recv() failed");

  // 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, BUFSIZ, 0);
    if (numBytesRcvd < 0)
      DieWithSystemMessage("recv() failed");
  }
  close(clntSocket); // Close client socket
}

int acceptTCPConnection(int server_sock)
{
    struct sockaddr_storage client_addr;
    socklen_t client_addrLen=sizeof(client_addr);
    int client_sock=accept(server_sock,(struct sockaddr *)&client_addr,&client_addrLen);
    if(client_sock<0)
        DieWithSystemMessage("accept() failed!");
    fputs("Handing client ",stdout);
    PrintSocketAddress((struct sockaddr*)&client_addr,stdout);
    fputc('\n',stdout);
    return 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!",service);
    }
    for(;;)
    {
        int client_sock=acceptTCPConnection(server_sock);
        HandleTCPClient(client_sock);
        close(client_sock);
    }
            
    return (EXIT_SUCCESS);
}

           
/* 
 * File:   DieWithMessage.h
 * Author: root
 *
 * Created on 2013年11月13日, 下午3:52
 */

#ifndef DIEWITHMESSAGE_H
#define	DIEWITHMESSAGE_H

#ifdef	__cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
 void  DieWithUserMessage(const char *msg,const char * detail)
 {
     fputs(msg,stderr);
     fputs(":",stderr);
     fputs(detail,stderr);
     fputs("\n",stderr);
     exit(1);
 }
 void DieWithSystemMessage(const char* msg)
 {
     perror(msg);
     exit(1);
 }



#ifdef	__cplusplus
}
#endif

#endif	/* DIEWITHMESSAGE_H */