- 論壇徽章:
- 0
|
用gcc tcpforwardport.c -o MyProxy編譯此程序后運(yùn)行效果如下:
./MyProxy 8000 80 172.16.100.218
accepting connections on port 8000
connect from 127.0.0.1
當(dāng)有用戶訪問本機(jī)的8000端口時(shí),MyProxy程序?qū)汛苏埱筠D(zhuǎn)發(fā)到172.16.100.218主機(jī)的80端口,即實(shí)現(xiàn)了一個(gè)http代理。
關(guān)于select函數(shù):
其函數(shù)原型為:
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
此函數(shù)的功能是由內(nèi)核檢測在timeout時(shí)間內(nèi),是否有readfds,writefds,exceptfds三個(gè)句柄集(file descriptors)里的某個(gè)句柄(file descriptor)的狀態(tài)符合尋求,即readfds句柄集里有句柄可讀或writefds句柄集里有可寫或exceptfds句柄集里有例外發(fā)生,任何一個(gè)有變化函數(shù)就立即返回,返回值為timeout發(fā)生狀態(tài)變化的句柄個(gè)數(shù)。
n是所有readfds,writefds,exceptfds三個(gè)句柄集(file descriptors)里編號最大值加1。比如:要檢測兩個(gè)socket句柄fd1和fd2在timeout時(shí)間內(nèi)是否分別可讀和可寫就可以這樣:
先把兩個(gè)句柄集(file descriptors)清零:
FD_ZERO (&readfds);
FD_ZERO (&writefds);
然后把fd1加入讀檢測集:
FD_SET (fd1, &readfds);
然后把fd2加入寫檢測集:
FD_SET (fd2, &writefds);
再給timeout設(shè)置值,timeout是這樣的一個(gè)結(jié)構(gòu):
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
你可以這樣賦值:
timeout.tv_sec=1;
timeout.tv_uec=0;
表示檢測在1秒鐘內(nèi)是否有句柄狀態(tài)發(fā)生變化。
如果有句柄發(fā)生變化,就可以用FD_ISSET檢測各個(gè)句柄,比如:
FD_ISSET (fd1, &readfds);//檢測是否fd1變成可讀的了
FD_ISSET (fd2, &writefds);//檢測是否fd2變成可寫的了
示意程序代碼如下:
- /*----------------------示意代碼開始--------------------------------------------*/
- fd1 = socket();//創(chuàng)建一個(gè)socket
- fd2 = socket();//創(chuàng)建一個(gè)socket
- while(1) {
- FD_ZERO (&readfds);
- FD_ZERO (&writefds);
- FD_SET (fd1, &readfds);
- FD_SET (fd2, &writefds);
- timeout.tv_sec=1;
- timeout.tv_uec=0;
- ret = select(fd1>fd2?(fd1+1):(fd2+1), &readfds, &writefds, NULL, &timeout);
- if(ret < 0) {printf("系統(tǒng)錯(cuò)誤,select出錯(cuò),錯(cuò)誤代碼:%d, 錯(cuò)誤信息:%s", errno, strerror(errno));}
- else if(ret == 0) {printf("select超時(shí)返回,沒有任何句柄狀態(tài)發(fā)生變化!");}
- //有句柄狀態(tài)發(fā)生了變化
- if(FD_ISSET(fd1, &readfds)) {
- fd1有數(shù)據(jù)可讀;
- fd1里的數(shù)據(jù)被讀出來;
- }
- if(FD_ISSET(fd2, &writefds)) {
- fd2可寫;
- fd2里發(fā)送數(shù)據(jù)給對方;
- }
- }
- /*----------------------示意代碼結(jié)束--------------------------------------------*/
復(fù)制代碼
經(jīng)常用到的幾個(gè)自定義函數(shù):
1、開啟監(jiān)聽的函數(shù)
- /*----------------------源代碼代碼開始--------------------------------------------*/
- int
- OpenSCPServer(int port, int total, int sendbuflen, int recvbuflen, int blockORnot, int reuseORnot) {
- /*************************關(guān)于本函數(shù)************************************
- *function_name: OpenSCPServer
- *參數(shù)說明:port整數(shù)型監(jiān)聽端口號,total整數(shù)型監(jiān)聽個(gè)數(shù),sendbuflen整數(shù)型發(fā)送緩沖區(qū)大小
- * recvbuflen整數(shù)型接收緩沖區(qū)大小,blockORnot整數(shù)型是否阻塞,reuseORnot整數(shù)型是否端口重用
- *purpose: 用來建立一個(gè)tcp服務(wù)端socket
- *tidied by: zhoulifa(zhoulifa@163.com) 周立發(fā)(http://zhoulifa.9999mb.com)
- Linux愛好者 Linux知識傳播者 SOHO族 開發(fā)者 最擅長C語言
- *date time:2006-07-05 20:00:00
- *Note: 任何人可以任意復(fù)制代碼并運(yùn)用這些文檔,當(dāng)然包括你的商業(yè)用途
- * 但請遵循GPL
- *Thanks to: Paul Sheer 感謝Paul Sheer在select_tut的man手冊里提供了這份源代碼
- *Hope:希望越來越多的人貢獻(xiàn)自己的力量,為科學(xué)技術(shù)發(fā)展出力
- *Note:要使用此函數(shù)需要自定義一個(gè)全局變量char errorMessage[1024];并包含GetCurrentTime.h頭文件
- *********************************************************************/
- int sockfd = 0, ret = 0, opt = 0, flags=1;
- struct sockaddr_in laddr;
- ret = sockfd = socket(PF_INET, SOCK_STREAM, 0);
- if(ret < 0) {
- sprintf(errorMessage, "OpenTCPServer socket() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
- return -1;
- }
- ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseORnot, sizeof(int));
- if(ret < 0) {
- sprintf(errorMessage, "OpenTCPServer setsockopt() reuse error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
- return -2;
- }
- ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuflen, sizeof(int));
- if ( ret < 0) {
- sprintf(errorMessage, "OpenTCPServer setsockopt() recvbuf error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
- return -3;
- }
- ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuflen, sizeof(int));
- if (ret < 0) {
- sprintf(errorMessage, "OpenTCPServer setsockopt() sendbuf error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
- return -4;
- }
- ioctl(sockfd,FIONBIO,&blockORnot);/*block or not*/
- laddr.sin_family = PF_INET;
- laddr.sin_port = htons(port);
- laddr.sin_addr.s_addr = INADDR_ANY;
- bzero(&(laddr.sin_zero), 8);
- ret = bind(sockfd, (struct sockaddr *)&laddr, sizeof(struct sockaddr));
- if(ret < 0) {
- sprintf(errorMessage, "OpenTCPServer bind() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
- close(sockfd);
- return -5;
- }
- ret = listen(sockfd, total);
- if(ret < 0) {
- sprintf(errorMessage, "OpenTCPServer listen() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
- close(sockfd);
- return -6;
- }
- sprintf(errorMessage, "OpenTCPServer opened on port.%d(%d) OK, socket(%d), buf(%d:%d)! %s", port, total, sockfd, sendbuflen, recvbuflen, GetCurrentTime(0, 0));
- return sockfd;
- }
- /*----------------------源代碼代碼結(jié)束--------------------------------------------*/
復(fù)制代碼
2、連接服務(wù)器的函數(shù)
- /*----------------------源代碼代碼開始--------------------------------------------*/
- int
- ConnectSCPServer(char * serverip, int serverport, int blockORnot) {
- /*************************關(guān)于本函數(shù)************************************
- *function_name: ConnectSCPServer
- *參數(shù)說明:serverip服務(wù)器IP地址或主機(jī)名,serverport服務(wù)器端口,blockORnot整數(shù)型是否阻塞
- *purpose: 用來建立一個(gè)tcp客戶端socket
- *tidied by: zhoulifa(zhoulifa@163.com) 周立發(fā)(http://zhoulifa.9999mb.com)
- Linux愛好者 Linux知識傳播者 SOHO族 開發(fā)者 最擅長C語言
- *date time:2006-07-05 20:40:00
- *Note: 任何人可以任意復(fù)制代碼并運(yùn)用這些文檔,當(dāng)然包括你的商業(yè)用途
- * 但請遵循GPL
- *Thanks to: Paul Sheer 感謝Paul Sheer在select_tut的man手冊里提供了這份源代碼
- *Hope:希望越來越多的人貢獻(xiàn)自己的力量,為科學(xué)技術(shù)發(fā)展出力
- *Note:要使用此函數(shù)需要自定義一個(gè)全局變量char errorMessage[1024];并包含自己編寫的GetCurrentTime.h頭文件
- *********************************************************************/
- int serversock = 0, ret = 0;
- unsigned long addr;
- struct sockaddr_in sin;
- struct hostent *he;
- if((he=gethostbyname(serverip))== 0) {
- sprintf(errorMessage, "ConnectSCPServer IP address '%s' error! return:-1 %s", serverip, GetCurrentTime(0, 0));
- return -1;
- }
- serversock = socket(PF_INET, SOCK_STREAM, 0);
- if(serversock == -1) {
- sprintf(errorMessage, "ConnectSCPServer socket() error! return:-2, errno=%d, errortext:'%s' %s", errno, strerror(errno), GetCurrentTime(0, 0));
- return -2;
- }
- ioctl(serversock, FIONBIO, &blockORnot); //block or not
- memset((char*)&sin, 0, sizeof(struct sockaddr_in));
- sin.sin_family = PF_INET;
- sin.sin_port = htons(serverport);
- sin.sin_addr = *((struct in_addr *)he->h_addr);
- ret = connect(serversock, (struct sockaddr *)&sin, sizeof(sin));
- if(ret == -1) {
- sprintf(errorMessage, "ConnectSCPServer connect() error! return:-3, errno=%d, errortext:'%s' %s", errno, strerror(errno), GetCurrentTime(0, 0));
- close(serversock);
- return -3;
- }
- return serversock;
- }
- /*----------------------源代碼代碼結(jié)束--------------------------------------------*/
復(fù)制代碼
3、發(fā)送數(shù)據(jù)函數(shù)Send
- /*----------------------源代碼代碼開始--------------------------------------------*/
- int
- Send(int sock, char * buf, size_t size, int flag, int timeout) {
- /*************************關(guān)于本函數(shù)************************************
- *function_name: Send
- *參數(shù)說明:sock整數(shù)型socket,buf待發(fā)送的內(nèi)容,size要發(fā)送的大小,flag發(fā)送選項(xiàng),timeout超時(shí)時(shí)間值
- *purpose: 用來通過一個(gè)socket在指定時(shí)間內(nèi)發(fā)送數(shù)據(jù)
- *tidied by: zhoulifa(zhoulifa@163.com) 周立發(fā)(http://zhoulifa.9999mb.com)
- Linux愛好者 Linux知識傳播者 SOHO族 開發(fā)者 最擅長C語言
- *date time:2006-07-05 20:58:00
- *Note: 任何人可以任意復(fù)制代碼并運(yùn)用這些文檔,當(dāng)然包括你的商業(yè)用途
- * 但請遵循GPL
- *Thanks to: Paul Sheer 感謝Paul Sheer在select_tut的man手冊里提供了這份源代碼
- *Hope:希望越來越多的人貢獻(xiàn)自己的力量,為科學(xué)技術(shù)發(fā)展出力
- *Note:要使用此函數(shù)需要自定義一個(gè)全局變量char errorMessage[1024];并包含自己編寫的GetCurrentTime.h頭文件
- *********************************************************************/
- int i = 0, ret = 0, intretry = 0;
- struct timeval tival;
- fd_set writefds;
- int maxfds = 0;
- tival.tv_sec = timeout;
- tival.tv_usec = 0;
- FD_ZERO(&writefds);
- if(sock > 0) {
- FD_SET(sock, &writefds);
- maxfds=((sock > maxfds)?sock:maxfds);
- }
- else {
- sprintf(errorMessage, "Send socket:%d error! return:-2 %s", sock, GetCurrentTime(0, 0));
- return -2;
- }
- ret = select(maxfds + 1, NULL, &writefds, NULL, &tival);
- if(ret <= 0) {
- if(ret < 0) sprintf(errorMessage, "Send socket:%d select() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
- else sprintf(errorMessage, "Send socket:%d select timeout(%d)! %s", sock, timeout, GetCurrentTime(0, 0));
- close(sock);
- return -3;
- }
- if(!(FD_ISSET(sock, &writefds))) {
- sprintf(errorMessage, "Send socket:%d not in writefds! %s", sock, GetCurrentTime(0, 0));
- close(sock);
- return -4;
- }
- while(i < size) {
- ret = send(sock, buf + i, size - i, flag);
- if(ret <= 0) {
- sprintf(errorMessage, "Send socket:%d send() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
- if (EINTR == errno)
- if(intretry < 10) {intretry++;continue;}
- else sprintf(errorMessage, "Send socket:%d send() error!EINTR 10 times! %s", sock, GetCurrentTime(0, 0));
- close(sock);
- return -1;
- }
- else i += ret;
- }
- sprintf(errorMessage, "Send socket:%d send() OK! %d/%d bytes sent! %s", sock, i, size, GetCurrentTime(0, 0));
- return i;
- }
- /*----------------------源代碼代碼結(jié)束--------------------------------------------*/
復(fù)制代碼
4、接收數(shù)據(jù)函數(shù)Recv
- /*----------------------源代碼代碼開始--------------------------------------------*/
- int
- Recv(int sock, char * buf, size_t size, int flag, int timeout) {
- /*************************關(guān)于本函數(shù)************************************
- *function_name: Recv
- *參數(shù)說明:sock整數(shù)型socket,buf接收數(shù)據(jù)的緩沖區(qū),size要接收數(shù)據(jù)的大小,flag接收選項(xiàng),timeout超時(shí)時(shí)間值
- *purpose: 用來從一個(gè)socket在指定時(shí)間內(nèi)讀取數(shù)據(jù)
- *tidied by: zhoulifa(zhoulifa@163.com) 周立發(fā)(http://zhoulifa.9999mb.com)
- Linux愛好者 Linux知識傳播者 SOHO族 開發(fā)者 最擅長C語言
- *date time:2006-07-05 21:10:00
- *Note: 任何人可以任意復(fù)制代碼并運(yùn)用這些文檔,當(dāng)然包括你的商業(yè)用途
- * 但請遵循GPL
- *Thanks to: Paul Sheer 感謝Paul Sheer在select_tut的man手冊里提供了這份源代碼
- *Hope:希望越來越多的人貢獻(xiàn)自己的力量,為科學(xué)技術(shù)發(fā)展出力
- *Note:要使用此函數(shù)需要自定義一個(gè)全局變量char errorMessage[1024];并包含自己編寫的GetCurrentTime.h頭文件
- *********************************************************************/
- int i = 0, ret = 0, intretry = 0;
- struct timeval tival;
- fd_set readfds;
- int maxfds = 0;
- tival.tv_sec = timeout;
- tival.tv_usec = 0;
- FD_ZERO(&readfds);
- if(sock > 0) {
- FD_SET(sock, &readfds);
- maxfds=((sock > maxfds)?sock:maxfds);
- }
- else {
- sprintf(errorMessage, "Recv socket:%d error! return:-2 %s", sock, GetCurrentTime(0, 0));
- return -2;
- }
- ret = select(maxfds + 1, &readfds, NULL, NULL, &tival);
- if(ret <= 0) {
- if(ret < 0) sprintf(errorMessage, "Recv socket:%d select() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
- else sprintf(errorMessage, "Recv socket:%d select timeout(%d)! %s", sock, timeout, GetCurrentTime(0, 0));
- close(sock);
- return -3;
- }
- if(!(FD_ISSET(sock, &readfds))) {
- sprintf(errorMessage, "Recv socket:%d not in readfds! %s", sock, GetCurrentTime(0, 0));
- close(sock);
- return -4;
- }
- while(i < size) {
- ret = recv(sock, buf + i, size - i, flag);
- if(ret <= 0){
- sprintf(errorMessage, "Recv socket:%d recv() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
- if(errno == EINTR)
- if(intretry < 10) {intretry++;continue;}
- else sprintf(errorMessage, "Recv socket:%d recv() error! EINTR 10 times! %s", sock, GetCurrentTime(0, 0));
- close(sock);
- return -1;
- }
- else i += ret;
- }
- sprintf(errorMessage, "Recv socket:%d recv() OK! %d/%d bytes received! %s", sock, i, size, GetCurrentTime(0, 0));
- return i;
- }
復(fù)制代碼
最后需要說明的是:我這里講到的源程序并不能實(shí)際地作為一個(gè)產(chǎn)品程序來用,實(shí)際情況下可能會(huì)有其它許多工作要做,比如可能要建立共享隊(duì)列來存放 socket里讀到的消息,也可能把發(fā)送消息先進(jìn)行排隊(duì)然后再調(diào)用Send函數(shù)。還有,如果不是全數(shù)字,在發(fā)送前一定要htonl轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,同理接收到后一定要先ntohl由網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換為主機(jī)字節(jié)序,否則對方發(fā)送過來的0x00000001在你這里可能是0x00010000,因?yàn)楦叩臀豁樞虿煌?br />
[ 本帖最后由 zhoulifa 于 2006-10-19 08:36 編輯 ] |
|