一个 Linux Windows下都可运行的 Socket 程序

用C实现的TCP socket连接/读/写操作。采用fcntl设置非阻塞式连接以实现connect超时处理;采用select方法来设置socket读写超时。此示例可被编译运行于Windows/unix系统。

 

源文件connector.c

原来的代码在windows下编译不通过,今天qzj问起才发现。因为加了异步的处理,没有对这部分代码进行兼容性处理。本着做学问一丝不苟嘀精神,重新修改了一下源代码。以下代码在VC++6和linux下编译执行通过

 

/*

 * on Unix:

 *    cc -c connector.c

 *    cc -o connector connector.o

 *

 * on Windows NT:

 *    open connector.c in Visual Studio

 *    press 'F7' to link -- a project to be created

 *    add wsock32.lib to the link section under project setting

 *    press 'F7' again

 *

 * running:

 *    type 'connector' for usage

 */


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <stdarg.h>

#include <errno.h>

#include <fcntl.h>

#ifdef WIN32

#include <winsock2.h>

#else

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/ioctl.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <netdb.h>

#endif


#ifndef INADDR_NONE

#define INADDR_NONE     0xffffffff

#endif

#define MAX_STRING_LEN  1024

#define BUFSIZE  2048


#ifndef WIN32

#define SOCKET int

#else

#define errno WSAGetLastError()

#define close(a) closesocket(a)

#define write(a, b, c) send(a, b, c, 0)

#define read(a, b, c) recv(a, b, c, 0)

#endif


char buf[BUFSIZE];


static char i_host[MAX_STRING_LEN];  /* site name */

static char i_port[MAX_STRING_LEN];  /* port number */


void err_doit(int errnoflag, const char *fmt, va_list ap);

void err_quit(const char *fmt, ...);

int tcp_connect(const char *host, const unsigned short port);

void print_usage();


//xnet_select x defines

#define READ_STATUS  0

#define WRITE_STATUS 1

#define EXCPT_STATUS 2


/*

s    - SOCKET

sec  - timeout seconds

usec - timeout microseconds

x    - select status

*/

SOCKET xnet_select(SOCKET s, int sec, int usec, short x)

{

 int st = errno;

 struct timeval to;

 fd_set fs;

 to.tv_sec = sec;

 to.tv_usec = usec;

 FD_ZERO(&fs);

 FD_SET(s, &fs);

 switch(x){

  case READ_STATUS:

  st = select(s+1, &fs, 0, 0, &to);

  break;

  case WRITE_STATUS:

  st = select(s+1, 0, &fs, 0, &to);

  break;

  case EXCPT_STATUS:

  st = select(s+1, 0, 0, &fs, &to);

  break;

 }

 return(st);

}


int tcp_connect(const char *host, const unsigned short port)

{

    unsigned long non_blocking = 1;

    unsigned long blocking = 0;

    int ret = 0;

    char * transport = "tcp";

    struct hostent      *phe;   /* pointer to host information entry    */

    struct protoent *ppe;       /* pointer to protocol information entry*/

    struct sockaddr_in sin;     /* an Internet endpoint address  */

    SOCKET s;                    /* socket descriptor and socket type    */

    int error;


#ifdef WIN32

    {

        WORD wVersionRequested;

        WSADATA wsaData;

        int err;

 

        wVersionRequested = MAKEWORD( 2, 0 );

 

        err = WSAStartup( wVersionRequested, &wsaData );

        if ( err != 0 ) {

            /* Tell the user that we couldn't find a usable */

            /* WinSock DLL.                               */

            printf("can't initialize socket library\n");

            exit(0);

        }

    }

#endif    

    

    memset(&sin, 0, sizeof(sin));

    sin.sin_family = AF_INET;

    

    if ((sin.sin_port = htons(port)) == 0)

        err_quit("invalid port \"%d\"\n", port);

    

    /* Map host name to IP address, allowing for dotted decimal */

    if ( phe = gethostbyname(host) )

        memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);

    else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )

        err_quit("can't get \"%s\" host entry\n", host);

    

    /* Map transport protocol name to protocol number */

    if ( (ppe = getprotobyname(transport)) == 0)

        err_quit("can't get \"%s\" protocol entry\n", transport);

    

    /* Allocate a socket */

    s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);

    if (s < 0)

        err_quit("can't create socket: %s\n", strerror(errno));

    

    /* Connect the socket with timeout */

#ifdef WIN32

    ioctlsocket(s,FIONBIO,&non_blocking);

#else

    ioctl(s,FIONBIO,&non_blocking);

#endif

    //fcntl(s,F_SETFL, O_NONBLOCK);

    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1){

        struct timeval tv; 

        fd_set writefds;

        // 设置连接超时时间

        tv.tv_sec = 10; // 秒数

        tv.tv_usec = 0; // 毫秒

        FD_ZERO(&writefds); 

        FD_SET(s, &writefds); 

        if(select(s+1,NULL,&writefds,NULL,&tv) != 0){ 

            if(FD_ISSET(s,&writefds)){

                int len=sizeof(error); 

                //下面的一句一定要,主要针对防火墙 

                if(getsockopt(s, SOL_SOCKET, SO_ERROR,  (char *)&error, &len) < 0)

                    goto error_ret; 

                if(error != 0) 

                    goto error_ret; 

            }

            else

                goto error_ret; //timeout or error happen 

        }

        else goto error_ret; ; 


#ifdef WIN32

        ioctlsocket(s,FIONBIO,&blocking);

#else

        ioctl(s,FIONBIO,&blocking);

#endif


    }

    else{

error_ret:

        close(s);

        err_quit("can't connect to %s:%d\n", host, port);

    }

    return s;

}


void err_doit(int errnoflag, const char *fmt, va_list ap)

{

    int errno_save;

    char buf[MAX_STRING_LEN];


    errno_save = errno; 

    vsprintf(buf, fmt, ap);

    if (errnoflag)

        sprintf(buf + strlen(buf), ": %s", strerror(errno_save));

    strcat(buf, "\n");

    fflush(stdout);

    fputs(buf, stderr);

    fflush(NULL);

    return;

}


/* Print a message and terminate. */

void err_quit(const char *fmt, ...)

{

    va_list ap;

    va_start(ap, fmt);

    err_doit(0, fmt, ap);

    va_end(ap);

    exit(1);

}


#ifdef WIN32

char *optarg;


char getopt(int c, char *v[], char *opts)

{

    static int now = 1;

    char *p;


    if (now >= c) return EOF;


    if (v[now][0] == '-' && (p = strchr(opts, v[now][1]))) {

        optarg = v[now+1];

        now +=2;

        return *p;

    }


    return EOF;

}


#else

extern char *optarg;

#endif


#define required(a) if (!a) { return -1; }


int init(int argc, char *argv[])

{

    char c;

    //int i,optlen;

    //int slashcnt;


    i_host[0]  =  '\0';

    i_port[0]  =  '\0';


    while ((c = getopt(argc, argv, "h:p:?")) != EOF) {

        if (c == '?')

            return -1;

        switch (c) { 

        case 'h':

            required(optarg);

            strcpy(i_host, optarg);

            break;

        case 'p':

            required(optarg);

            strcpy(i_port, optarg);

            break;

        default:

            return -1;

        }

    }


    /* 

     * there is no default value for hostname, port number, 

     * password or uri

     */

    if (i_host[0] == '\0' || i_port[0] == '\0')

        return -1;


    return 1;

}


void print_usage()

{

    char *usage[] =

    {

        "Usage:",

        "    -h    host name",

        "    -p    port",

        "example:",

        "    -h 127.0.0.1 -p 4001",

    };   

    int i;


    for (i = 0; i < sizeof(usage) / sizeof(char*); i++)

        printf("%s\n", usage[i]);

    

    return;

}


int main(int argc, char *argv[])

{

    SOCKET fd;

    int n;


    /* parse command line etc ... */

    if (init(argc, argv) < 0) {

        print_usage();

        exit(1);

    }


    buf[0] = '\0';


    /* pack the info into the buffer */     

    strcpy(buf, "HelloWorld");


    /* make connection to the server */

    fd = tcp_connect(i_host, (unsigned short)atoi(i_port));


    if(xnet_select(fd, 0, 500, WRITE_STATUS)>0){

        /* send off the message */

        write(fd, buf, strlen(buf));

    }

    else{

        err_quit("Socket I/O Write Timeout %s:%s\n", i_host, i_port);

    }


    if(xnet_select(fd, 3, 0, READ_STATUS)>0){

        /* display the server response */

        printf("Server response:\n");

        n = read(fd, buf, BUFSIZE);

        buf[n] = '\0';

        printf("%s\n", buf);

    }

    else{

        err_quit("Socket I/O Read Timeout %s:%s\n", i_host, i_port);

    }

    close(fd);


#ifdef WIN32

    WSACleanup();

#endif


    return 0;

}

同时mark几个有用的url:

http://people.web.psi.ch/rohrer_u/sample1.htm   Demo program for remote CAMAC access via TCP/IP

http://www.lsword.net/code/list.asp?id=1383  将Socket应用程序从Unix向Windows移植中应注意的几点问题


原文:http://www.cnblogs.com/cy163/archive/2009/09/03/1559513.html

posted @ 2012-10-11 20:41  Bmi  阅读(569)  评论(0编辑  收藏  举报