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

 



源文件connector.c

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



1. /*  
2.
3. * on Unix:
4.
5. * cc -c connector.c
6.
7. * cc -o connector connector.o
8.
9. *
10.
11. * on Windows NT:
12.
13. * open connector.c in Visual Studio
14.
15. * press 'F7' to link -- a project to be created
16.
17. * add wsock32.lib to the link section under project setting
18.
19. * press 'F7' again
20.
21. *
22.
23. * running:
24.
25. * type 'connector' for usage
26.
27. */
28.
29.
30.
#include <stdio.h>

31.
32.
#include <stdlib.h>

33.
34.
#include <string.h>

35.
36.
#include <stdarg.h>

37.
38.
#include <errno.h>

39.
40.
#include <fcntl.h>

41.
42.
#ifdef WIN32

43.
44.
#include <winsock2.h>

45.
46.
#else

47.
48.
#include <unistd.h>

49.
50.
#include <sys/types.h>

51.
52.
#include <sys/socket.h>

53.
54.
#include <sys/ioctl.h>

55.
56.
#include <netinet/in.h>

57.
58.
#include <arpa/inet.h>

59.
60.
#include <netdb.h>

61.
62.
#endif

63.
64.
65.
#ifndef INADDR_NONE

66.
67.
#define INADDR_NONE 0xffffffff

68.
69.
#endif

70.
71.
#define MAX_STRING_LEN 1024

72.
73.
#define BUFSIZE 2048

74.
75.
76.
#ifndef WIN32

77.
78.
#define SOCKET int

79.
80.
#else

81.
82.
#define errno WSAGetLastError()

83.
84.
#define close(a) closesocket(a)

85.
86.
#define write(a, b, c) send(a, b, c, 0)

87.
88.
#define read(a, b, c) recv(a, b, c, 0)

89.
90.
#endif

91.
92.
93.
char buf[BUFSIZE];

94.
95.
96.
static char i_host[MAX_STRING_LEN]; /* site name */

97.
98.
static char i_port[MAX_STRING_LEN]; /* port number */

99.
100.
101.
void err_doit(int errnoflag, const char *fmt, va_list ap);

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

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

106.
107.
void print_usage();

108.
109.
110.
//xnet_select x defines

111.
112.
#define READ_STATUS 0

113.
114.
#define WRITE_STATUS 1

115.
116.
#define EXCPT_STATUS 2

117.
118.
119.
/*

120.
121. s - SOCKET
122.
123. sec - timeout seconds
124.
125. usec - timeout microseconds
126.
127. x - select status
128.
129. */
130.
131.
SOCKET xnet_select(SOCKET s, int sec, int usec, short x)

132.
133. {
134.
135.
int st = errno;

136.
137.
struct timeval to;

138.
139. fd_set fs;
140.
141. to.tv_sec = sec;
142.
143. to.tv_usec = usec;
144.
145. FD_ZERO(&fs);
146.
147. FD_SET(s, &fs);
148.
149.
switch(x){

150.
151.
case READ_STATUS:

152.
153. st = select(s+1, &fs, 0, 0, &to);
154.
155.
break;

156.
157.
case WRITE_STATUS:

158.
159. st = select(s+1, 0, &fs, 0, &to);
160.
161.
break;

162.
163.
case EXCPT_STATUS:

164.
165. st = select(s+1, 0, 0, &fs, &to);
166.
167.
break;

168.
169. }
170.
171.
return(st);

172.
173. }
174.
175.
176.
int tcp_connect(const char *host, const unsigned short port)

177.
178. {
179.
180.
unsigned long non_blocking = 1;

181.
182.
unsigned long blocking = 0;

183.
184.
int ret = 0;

185.
186.
char * transport = "tcp";

187.
188.
struct hostent *phe; /* pointer to host information entry */

189.
190.
struct protoent *ppe; /* pointer to protocol information entry*/

191.
192.
struct sockaddr_in sin; /* an Internet endpoint address */

193.
194.
SOCKET s; /* socket descriptor and socket type */

195.
196.
int error;

197.
198.
199.
#ifdef WIN32

200.
201. {
202.
203.
WORD wVersionRequested;

204.
205. WSADATA wsaData;
206.
207.
int err;

208.
209.
210.
211. wVersionRequested = MAKEWORD( 2, 0 );
212.
213.
214.
215. err = WSAStartup( wVersionRequested, &wsaData );
216.
217.
if ( err != 0 ) {

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

220.
221.
/* WinSock DLL. */

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

224.
225. exit(0);
226.
227. }
228.
229. }
230.
231.
#endif

232.
233.
234.
235.
memset(&sin, 0, sizeof(sin));

236.
237. sin.sin_family = AF_INET;
238.
239.
240.
241.
if ((sin.sin_port = htons(port)) == 0)

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

244.
245.
246.
247.
/* Map host name to IP address, allowing for dotted decimal */

248.
249.
if ( phe = gethostbyname(host) )

250.
251. memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
252.
253.
else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )

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

256.
257.
258.
259.
/* Map transport protocol name to protocol number */

260.
261.
if ( (ppe = getprotobyname(transport)) == 0)

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

264.
265.
266.
267.
/* Allocate a socket */

268.
269. s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
270.
271.
if (s < 0)

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

274.
275.
276.
277.
/* Connect the socket with timeout */

278.
279.
#ifdef WIN32

280.
281. ioctlsocket(s,FIONBIO,&non_blocking);
282.
283.
#else

284.
285. ioctl(s,FIONBIO,&non_blocking);
286.
287.
#endif

288.
289.
//fcntl(s,F_SETFL, O_NONBLOCK);

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

292.
293.
struct timeval tv;

294.
295. fd_set writefds;
296.
297.
// 设置连接超时时间

298.
299.
tv.tv_sec = 10; // 秒数

300.
301.
tv.tv_usec = 0; // 毫秒

302.
303. FD_ZERO(&writefds);
304.
305. FD_SET(s, &writefds);
306.
307.
if(select(s+1,NULL,&writefds,NULL,&tv) != 0){

308.
309.
if(FD_ISSET(s,&writefds)){

310.
311.
int len=sizeof(error);

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

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

316.
317.
goto error_ret;

318.
319.
if(error != 0)

320.
321.
goto error_ret;

322.
323. }
324.
325.
else

326.
327.
goto error_ret; //timeout or error happen

328.
329. }
330.
331.
else goto error_ret; ;

332.
333.
334.
#ifdef WIN32

335.
336. ioctlsocket(s,FIONBIO,&blocking);
337.
338.
#else

339.
340. ioctl(s,FIONBIO,&blocking);
341.
342.
#endif

343.
344.
345. }
346.
347.
else{

348.
349. error_ret:
350.
351. close(s);
352.
353.
err_quit("can't connect to %s:%d\n", host, port);

354.
355. }
356.
357.
return s;

358.
359. }
360.
361.
362.
void err_doit(int errnoflag, const char *fmt, va_list ap)

363.
364. {
365.
366.
int errno_save;

367.
368.
char buf[MAX_STRING_LEN];

369.
370.
371. errno_save = errno;
372.
373. vsprintf(buf, fmt, ap);
374.
375.
if (errnoflag)

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

378.
379.
strcat(buf, "\n");

380.
381. fflush(stdout);
382.
383. fputs(buf, stderr);
384.
385. fflush(NULL);
386.
387.
return;

388.
389. }
390.
391.
392.
/* Print a message and terminate. */

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

395.
396. {
397.
398.
va_list ap;

399.
400. va_start(ap, fmt);
401.
402. err_doit(0, fmt, ap);
403.
404. va_end(ap);
405.
406. exit(1);
407.
408. }
409.
410.
411.
#ifdef WIN32

412.
413.
char *optarg;

414.
415.
416.
char getopt(int c, char *v[], char *opts)

417.
418. {
419.
420.
static int now = 1;

421.
422.
char *p;

423.
424.
425.
if (now >= c) return EOF;

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

429.
430. optarg = v[now+1];
431.
432. now +=2;
433.
434.
return *p;

435.
436. }
437.
438.
439.
return EOF;

440.
441. }
442.
443.
444.
#else

445.
446.
extern char *optarg;

447.
448.
#endif

449.
450.
451.
#define required(a) if (!a) { return -1; }

452.
453.
454.
int init(int argc, char *argv[])

455.
456. {
457.
458.
char c;

459.
460.
//int i,optlen;

461.
462.
//int slashcnt;

463.
464.
465.
i_host[0] = '\0';

466.
467.
i_port[0] = '\0';

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

471.
472.
if (c == '?')

473.
474.
return -1;

475.
476.
switch (c) {

477.
478.
case 'h':

479.
480. required(optarg);
481.
482. strcpy(i_host, optarg);
483.
484.
break;

485.
486.
case 'p':

487.
488. required(optarg);
489.
490. strcpy(i_port, optarg);
491.
492.
break;

493.
494.
default:

495.
496.
return -1;

497.
498. }
499.
500. }
501.
502.
503.
/*

504.
505. * there is no default value for hostname, port number,
506.
507. * password or uri
508.
509. */
510.
511.
if (i_host[0] == '\0' || i_port[0] == '\0')

512.
513.
return -1;

514.
515.
516.
return 1;

517.
518. }
519.
520.
521.
void print_usage()

522.
523. {
524.
525.
char *usage[] =

526.
527. {
528.
529.
"Usage:",

530.
531.
" -h host name",

532.
533.
" -p port",

534.
535.
"example:",

536.
537.
" -h 127.0.0.1 -p 4001",

538.
539. };
540.
541.
int i;

542.
543.
544.
for (i = 0; i < sizeof(usage) / sizeof(char*); i++)

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

547.
548.
549.
550.
return;

551.
552. }
553.
554.
555.
int main(int argc, char *argv[])

556.
557. {
558.
559. SOCKET fd;
560.
561.
int n;

562.
563.
564.
/* parse command line etc ... */

565.
566.
if (init(argc, argv) < 0) {

567.
568. print_usage();
569.
570. exit(1);
571.
572. }
573.
574.
575.
buf[0] = '\0';

576.
577.
578.
/* pack the info into the buffer */

579.
580.
strcpy(buf, "HelloWorld");

581.
582.
583.
/* make connection to the server */

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

586.
587.
588.
if(xnet_select(fd, 0, 500, WRITE_STATUS)>0){

589.
590.
/* send off the message */

591.
592. write(fd, buf, strlen(buf));
593.
594. }
595.
596.
else{

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

599.
600. }
601.
602.
603.
if(xnet_select(fd, 3, 0, READ_STATUS)>0){

604.
605.
/* display the server response */

606.
607.
printf("Server response:\n");

608.
609. n = read(fd, buf, BUFSIZE);
610.
611.
buf[n] = '\0';

612.
613.
printf("%s\n", buf);

614.
615. }
616.
617.
else{

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

620.
621. }
622.
623. close(fd);
624.
625.
626.
#ifdef WIN32

627.
628. WSACleanup();
629.
630.
#endif

631.
632.
633.
return 0;

634.
635. }
636.
637.


/* * 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移植中应注意的几点问题