一个 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