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