Linux是一个可靠性非常高的操作系统,但是所有用过Linux的朋友都会感觉到, Linux和Windows这样的"傻瓜"操作系统(这里丝毫没有贬低Windows的意思,相反这应该是Windows的优点)相比,后者无疑在易操作 性上更胜一筹。但是为什么又有那么多的爱好者钟情于Linux呢,当然自由是最吸引人的一点,另外Linux强大的功能也是一个非常重要的原因,尤其是 Linux强大的网络功能更是引人注目。放眼今天的WAP业务、银行网络业务和曾经红透半边天的电子商务,都越来越倚重基于Linux的解决方案。因此 Linux网络编程是非常重要的,而且当我们一接触到Linux网络编程,我们就会发现这是一件非常有意思的事情,因为以前一些关于网络通信概念似是而非的地方,在这一段段代码面前马上就豁然开朗了。在刚开始学习编程的时候总是让人感觉有点理不清头绪,不过只要多读几段代码,很快我们就能体会到其中的乐趣 了。下面我就从一段Proxy源代码开始,谈谈如何进行Linux网络编程。

首先声明,这段源代码不是我编写的,让我们感谢这位名叫Carl Harris的大虾,是他编写了这段代码并将其散播到网上供大家学习讨论。这段代码虽然只是描述了最简单的proxy操作,但它的确是经典,它不仅清晰地 描述了客户机/服务器系统的概念,而且几乎包括了Linux网络编程的方方面面,非常适合Linux网络编程的初学者学习。
这段Proxy程序的用法是这样的,我们可以使用这个proxy登录其它主机的服务端口。假如编译后生成了名为Proxy的可执行文件,那么命令及其参数的描述为:
./Proxy <proxy_port> <remote_host> <service_port>
其中参数proxy_port是指由我们指定的代理服务器端口。参数remote_host是指我们希望连接的远程主机的主机名,IP地址也同样有 效。这个主机名在网络上应该是唯一的,如果您不确定的话,可以在远程主机上使用uname -n命令查看一下。参数service_port是远程主机可提供的服务名,也可直接键入服务对应的端口号。这个命令的相应操作是将代理服务器的 proxy_port端口绑定到remote_host的service_port端口。然后我们就可以通过代理服务器的proxy_port端口访问 remote_host了。例如一台计算机,网络主机名是legends,IP地址为10.10.8.221,如果在我的计算机上执行:
[root@lee /root]#./proxy 8000 legends telnet
那么我们就可以通过下面这条命令访问legends的telnet端口。

以下代码有多次修改。运行环境:Ubuntu 9.04 Server Gcc 4.3

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <netdb.h>
#define TCP_PROTO "tcp"
int proxy_port;
struct sockaddr_in hostaddr;
extern int errno;
//extern char *sys_errlist[];
void parse_args(int argc, char **argv);
void daemonize(int servfd);
void do_proxy(int usersocket);
void reap_status(void);
void errorout(char *msg); typedef void Sigfunc(int); int main(int argc, char **argv)
{
int clilen;
pid_t childpid; int sockfd, newsockfd;
struct sockaddr_in servaddr, cliaddr;
parse_args(argc, argv);
bzero((char *) &servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = proxy_port; if ((sockfd = socket(AF_INET, SOCK_STREAM, )) < ) {
fputs("failed to create server socket /r/n", stderr);
exit();
} if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < ) {
fputs("failed to bind server socket to specified port /r/n", stderr);
exit();
} listen(sockfd, );
daemonize(sockfd); for(;;) {
clilen = sizeof(cliaddr);
newsockfd = accept(sockfd, (struct sockaddr *) &cliaddr, &clilen);
if (newsockfd < && errno == EINTR)
continue;
else if (newsockfd < )
errorout("failed to accept connection"); if ((childpid = fork()) == ) {
close(sockfd);
do_proxy(newsockfd);
exit();
}
close(newsockfd);
}
} /***********************************************************************
function:parse_args
description: Parse the command line args.
arguments:argc, argv you know what these are.
return value: none.
calls:none.
globals:writes proxy_port, writes hostaddr.
**********************************************************************/ void parse_args(int argc, char ** argv)
{
int i;
struct hostent *hostp;
struct servent *servp;
unsigned long inaddr;
struct {
char proxy_port[];
char isolated_host[];
char service_name[];
} pargs; if (argc < ) {
printf("usage: %s <proxy-port> <host> <service-name|port-number> /r/n", argv[]);
exit();
} strcpy(pargs.proxy_port, argv[]);
strcpy(pargs.isolated_host, argv[]);
strcpy(pargs.service_name, argv[]); for (i = ; i < strlen(pargs.proxy_port); i++)
if (!isdigit(*(pargs.proxy_port + i))) break; if (i == strlen(pargs.proxy_port))
proxy_port = htons(atoi(pargs.proxy_port));
else {
printf("%s: invalid proxy port /r/n", pargs.proxy_port);
exit();
} bzero(&hostaddr, sizeof(hostaddr));
hostaddr.sin_family = AF_INET;
if ((inaddr = inet_addr(pargs.isolated_host)) != INADDR_NONE)
bcopy(&inaddr, &hostaddr.sin_addr, sizeof(inaddr));
else if ((hostp = gethostbyname(pargs.isolated_host)) != NULL)
bcopy(hostp->h_addr, &hostaddr.sin_addr, hostp->h_length);
else {
printf("%s: unknown host /r/n", pargs.isolated_host);
exit();
} if ((servp = getservbyname(pargs.service_name, TCP_PROTO)) != NULL)
hostaddr.sin_port = servp->s_port;
else if (atoi(pargs.service_name) > )
hostaddr.sin_port = htons(atoi(pargs.service_name));
else {
printf("%s: invalid/unknown service name or port number /r/n", pargs.service_name);
exit();
}
} /*****************************************************************
function: daemonize
description: detach the server process from the current context,
creating a pristine, predictable environment in which it will execute.
arguments: servfd file descriptor in use by server.
return value: none
calls: none
globals: none
*****************************************************************/
void daemonize(int servfd)
{
pid_t childpid;
int fd, fdtablesize;
/* ignore terminal I/O, stop signals */
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN); /* fork to put us in the background (whether or not the user specified
'&' on the command line */
if ((childpid = fork()) < ) {
fputs("failed to fork first child /r/n", stderr);
exit();
}
else if (childpid > )
exit(); if (setpgrp(, getpid()) < ) {
fputs("failed to become process group leader/r/n", stderr);
exit();
} if ((fd = open("/dev/tty", O_RDWR)) >= ) {
ioctl(fd, TIOCNOTTY, NULL);
close(fd);
} for(fd= , fdtablesize = getdtablesize(); fd < fdtablesize; fd++)
if (fd != servfd) close(fd); chdir("/"); umask();
signal(SIGCLD, (Sigfunc *)reap_status);
} /*****************************************************************
function:reap_status
desc: Handle a SIGCLD signal by reaping the exit status of the
perished child, and discarding it.
arguments:none
return value : none.
calls: none
globals : none
*******************************************************************/
void reap_status(void)
{
pid_t pid;
union wait status;
while ((pid = wait3(&status, WNOHANG, NULL)) > );
} /******************************************************************
function : do_proxy
desc : does the actual work of virtually connecting a client to the
telnet service on the isolated host.
arguments: usersockfd socket to which the client is connected.
return value : none
calls : none
globals : reads hostaddr.
*******************************************************************/ void do_proxy(int usersockfd)
{
int isosockfd;
fd_set rdfdset;
int connstat;
int iolen;
char buf[];
/*open a socket to connect to the isolated host */
if ((isosockfd = socket(AF_INET, SOCK_STREAM, )) < )
errorout("failed to create socket to host");
connstat = connect(isosockfd, (struct sockaddr *)&hostaddr, sizeof(hostaddr)); switch (connstat){
case : break;
case ETIMEDOUT:
case ECONNREFUSED:
case ENETUNREACH:
strcpy(buf, strerror(errno));
strcat(buf, "/r/n");
write(usersockfd, buf, strlen(buf));
close(usersockfd);
exit();
break;
default:
errorout("failed to connect to host");
} for(;;) {
/* Select for readability on either of our two sockedts */
FD_ZERO(&rdfdset);
FD_SET(usersockfd, &rdfdset);
FD_SET(isosockfd, &rdfdset);
if (select(FD_SETSIZE, &rdfdset, NULL, NULL, NULL) < )
errorout("Select failed"); /* is the client sending data? */
if (FD_ISSET(usersockfd, &rdfdset)) {
/* zero length means the client disconnected */
if ((iolen = read(usersockfd, buf, sizeof(buf))) <= ) break;
write(isosockfd, buf, iolen);
} /*is the host sending data? */
if (FD_ISSET(isosockfd, &rdfdset)) {
if ((iolen = read(isosockfd, buf, sizeof(buf))) <= ) break;
write(usersockfd, buf, iolen);
}
}
close(isosockfd);
close(usersockfd);
} /**************************************************************
function : errorout
desc : displays an error message on the console and kill the
current process.
arguments : msg-- message to be displayed.
return value : none;
calls : none
globals: none
**************************************************************/
void errorout(char *msg)
{
FILE *console;
console = fopen("/dev/console", "a");
fprintf(console, "proxyd: %s /r/n", msg);
fclose(console);
exit();
}

Linux网络编程:一个简单的正向代理服务器的实现的更多相关文章

  1. linux网络编程-一个简单的线程池(41)

    有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带来的开销,我们可以使用线程池 1.线程池拥有若干个线程,是线程的集合,线程池中的线程数目有严格的要求,用于执行大量的相对短 ...

  2. [Python网络编程]一个简单的TCP时间服务器

    服务器端: 1.创建一个面向网络的TCP套接字对象socket, 2.绑定地址和端口 3.监听 4.当有客户端连接时候,接受连接并给此连接分配一个新的套接字 5.当客户端发送空信息时候,关闭新分配的套 ...

  3. linux网络编程之简单的线程池实现

    转眼间离15年的春节越来越近了,还有两周的工作时间貌似心已经不在异乡了,期待与家人团聚的日子,当然最后两周也得坚持站好最后一班岗,另外期待的日子往往是心里不能平静的,越是想着过年,反而日子过得越慢,于 ...

  4. Python网络编程 - 一个简单的客户端Get请求程序

    import socket target_host = "www.baidu.com" target_port = 80 # create a socket object clie ...

  5. Linux网络编程简单示例

    linux 网络编程是通过socket(套接字)接口实现,Socket是一种文件描述符,socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭& ...

  6. Proxy源代码分析——谈谈如何学习Linux网络编程

    Linux是一个可靠性非常高的操作系统,但是所有用过Linux的朋友都会感觉到, Linux和Windows这样的"傻瓜"操作系统(这里丝毫没有贬低Windows的意思,相反这应该 ...

  7. Proxy源代码分析--谈谈如何学习Linux网络编程

    http://blog.csdn.net/cloudtech/article/details/1823531 Linux是一个可靠性非常高的操作系统,但是所有用过Linux的朋友都会感觉到,Linux ...

  8. 【linux草鞋应用编程系列】_5_ Linux网络编程

    一.网络通信简介   第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章.   二.linux网络通信     在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...

  9. linux网络编程-(socket套接字编程UDP传输)

    今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...

随机推荐

  1. [Leetcode] single number ii 找单个数

    Given an array of integers, every element appears three times except for one. Find that single one. ...

  2. 洛谷 P2446 [SDOI2010]大陆争霸 解题报告

    P2446 [SDOI2010]大陆争霸 题目背景 在一个遥远的世界里有两个国家:位于大陆西端的杰森国和位于大陆东端的克里斯国.两个国家的人民分别信仰两个对立的神:杰森国信仰象征黑暗和毁灭的神曾·布拉 ...

  3. Warning: Received `false` for a non-boolean attribute `xxx`.

    React对boolean类型的attribute的识别方式问题,可以采用以下方法解决: xxx={value ? 1 : 0} 改成数字的写法,不用布尔值. 具体可以参考:https://githu ...

  4. redux的bindActionCreators

    bindActionCreators是redux的一个API,作用是将单个或多个ActionCreator转化为dispatch(action)的函数集合形式. 开发者不用再手动dispatch(ac ...

  5. ContestHunter暑假欢乐赛 SRM 05

    T1 组合数,求一下乘法逆元就行了 没取模 没1LL* 爆零了 T2 让最大子段和最小就行,跑最大子段和的时候若超过S就弹出堆中最大的数,每次有负数加进来不断弹出最小的数相加重新加进堆直到为正数,因为 ...

  6. 000 Python常识与快捷键(未完)

    1.Python控制台IDLE的快捷键 Alt + N :返回开始输入的第一条语句 Alt + P :返回刚刚输入的上一条语句 Tab:制表符,用于缩进或补全内容,是Python语法格式的灵魂,作用涵 ...

  7. [技巧篇]02.关于MyBatis存取图片到MySQL数据Blob字段

  8. java发送邮件(一)

    一:前言 一直想做有关java发邮件的功能,但是了一直没有成功,特别的无语啊,所以今天有时间我终于成功了啊,虽然是最简单的,但是还是记载下来吧! 二:内容 这里主要需要的是spring-context ...

  9. ajax 请求数据的两种方法

    实现ajax 异步访问网络的方法有两个.第一个是原始的方法,第二个是利用jquery包的 原始的方法不用引入jquery包,只需在html中编写script 片段 这里我演示的是一个传递参数查询的例子 ...

  10. 洛谷 P3709 大爷的字符串题

    https://www.luogu.org/problem/show?pid=3709 题目背景 在那遥远的西南有一所学校 /*被和谐部分*/ 然后去参加该省省选虐场 然后某蒟蒻不会做,所以也出了一个 ...