分享
 
 
 

BSD Socket 简易入门手册

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

BSD Socket 简易入门手册

翻译:Wilbur Lang

介绍

类比 (什么是 socket ?)

装上你的新电话(怎样侦听?)

拨号 (如何调用 socket)

谈话(如何通过 sockets 交谈)

挂起(结束)

世界语(交流的语言很重要)

未来在你的掌握了(下一步?)

介绍

当你进入 UNIX 的神秘世界后,立刻会发现越来越多的东西难以理解。对于大多数人来说,BSD socket

的概念就是其中一个。这是一个很短的教程来解释他们是什么、他们如何工作并给出一些简单的代码来解释如何使

用他们。

类比 (什么是 socket ?)

socket 是进行程序间通讯(IPC)的 BSD 方法。这意味着 socket 用来让一个进程和其他的进程互通信息,就象我们

用电话来和其他的人交流一样。

用电话来比喻是很恰当的,我们在后面将一直用电话这个概念来描叙 socket 。

装上你的新电话(怎样侦听?)

一个人要能够收到别人打给他的电话,首先他要装上一门电话。同样,你必须先建立 socket 以侦听线路。这个过

程包含几个步骤。首先,你要建立一个新的

socket,就象先装上电话一样。socket() 命令就完成这个工作。

因为 sockets 有几种类型,你要注明你要建立什么类型的。你要做一个选择是 socket 的地址格式。如同电话有音

频和脉冲两种形式一样,socket

有两个最重要的选项是 AF_UNIX 和 IAF_INET。AF_UNIX 就象 UNIX 路径名一样识别 sockets。这种形式对于在同

一台机器上的 IPC

很有用。而 AF_INET 使用象 192.9.200.10 这样被点号隔开的四个十进制数字的地址格式。除了机器地址以外,还

可以利用端口号来允许每台机器上的多个

AF_INET socket。我们这里将着重于 AF_INET 方式,因为他很有用并广泛使用。

另外一个你必须提供的参数是 socket 的类型。两个重要的类型是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM

表明数据象字符流一样通过 socket 。而 SOCK_DGRAM 则表明数据将是数据报(datagrams)的形式。我们将讲解

SOCK_STREAM

sockets,他很常见并易于使用。

在建立 socket 后,我们就要提供 socket 侦听的地址了。就象你还要个电话号码来接电话一样。bind() 函数来处

理这件事情。

SOCK_STREAM sockets 让连接请求形成一个队列。如果你忙于处理一个连接,别的连接请求将一直等待到该连接处

理完毕。listen()

函数用来设置最大不被拒绝的请求数(一般为5个)。一般最好不要使用 listen() 函数。

下面的代码说明如何利用 socket()、 bind() 和 listen() 函数建立连接并可以接受数据。

/* code to establish a socket; originally from bzs@bu-cs.bu.edu

*/

int establish(unsigned short portnum)

{ char myname[MAXHOSTNAME+1];

int s;

struct sockaddr_in sa;

struct hostent *hp;

memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear our address */

gethostname(myname, MAXHOSTNAME); /* who are we? */

hp= gethostbyname(myname); /* get our address info */

if (hp == NULL) /* we don't exist !? */

return(-1);

sa.sin_family= hp->h_addrtype; /* this is our host address */

sa.sin_port= htons(portnum); /* this is our port number */

if ((s= socket(AF_INET, SOCK_STREAM, 0)) < 0) /* create socket */

return(-1);

if (bind(s,&sa,sizeof(struct sockaddr_in)) < 0) {

close(s);

return(-1); /* bind address to socket */

}

listen(s, 3); /* max # of queued connects */

return(s);

}

在建立完 socket 后,你要等待对该 socket 的调用了。accept() 函数为此目的而来。调用 accept()

如同在电话铃响后提起电话一样。Accept() 返回一个新的连接到调用方的 socket 。

下面的代码演示使用是个演示。

/* wait for a connection to occur on a socket created with establish()

*/

int get_connection(int s)

{ int t; /* socket of connection */

if ((t = accept(s,NULL,NULL)) < 0) /* accept connection if there is one */

return(-1);

return(t);

}

和电话不同的是,在你处理先前的连接的时候,你还可以接受调用。为此,一般用 fork 来处理每个连接。下面的

代码演示如何使用 establish() 和

get_connection() 来处理多个连接。

#include <errno.h> /* obligatory includes */

#include <signal.h>

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/wait.h>

#include <netinet/in.h>

#include <netdb.h>

#define PORTNUM 50000 /* random port number, we need something */

void fireman(void);

void do_something(int);

main()

{ int s, t;

if ((s= establish(PORTNUM)) < 0) { /* plug in the phone */

perror("establish");

exit(1);

}

signal(SIGCHLD, fireman); /* this eliminates zombies */

for (;;) { /* loop for phone calls */

if ((t= get_connection(s)) < 0) { /* get a connection */

if (errno == EINTR) /* EINTR might happen on accept(), */

continue; /* try again */

perror("accept"); /* bad */

exit(1);

}

switch(fork()) { /* try to handle connection */

case -1 : /* bad news. scream and die */

perror("fork");

close(s);

close(t);

exit(1);

case 0 : /* we're the child, do something */

close(s);

do_something(t);

exit(0);

default : /* we're the parent so look for */

close(t); /* another connection */

continue;

}

}

}

/* as children die we should get catch their returns or else we get

* zombies, A Bad Thing. fireman() catches falling children.

*/

void fireman(void)

{

while (waitpid(-1, NULL, WNOHANG) > 0)

;

}

/* this is the function that plays with the socket. it will be called

* after getting a connection.

*/

void do_something(int s)

{

/* do your thing with the socket here

:

:

*/

}

拨号 (如何调用 socket)

现在你应该知道如何建立 socket 来接受调用了。那么如何调用呢?和电话一样,你要先有个电话。用 socket()

函数来完成这件事情,就象建立侦听的

socket 一样。

在给 socket 地址后,你可以用 connect() 函数来连接侦听的 socket 了。下面是一段代码。

int call_socket(char *hostname, unsigned short portnum)

{ struct sockaddr_in sa;

struct hostent *hp;

int a, s;

if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host's */

errno= ECONNREFUSED; /* address? */

return(-1); /* no */

}

memset(&sa,0,sizeof(sa));

memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */

sa.sin_family= hp->h_addrtype;

sa.sin_port= htons((u_short)portnum);

if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0) /* get socket */

return(-1);

if (connect(s,&sa,sizeof sa) < 0) { /* connect */

close(s);

return(-1);

}

return(s);

}

这个函数返回一个可以流过数据的 socket 。

谈话(如何通过 sockets 交谈)

好了,你在要传输数据的双方建立连接了,现在该传输数据了。read() 和 write() 函数来处理吧。除了在 socket

读写和文件读写中的一个区别外,和处理一般的文件一样。区别是你一般不能得到你所要的数目的数据。所以你要

一直循环到你需要的数据的到来。一个简单的例子:将一定的数据读到缓存。

int read_data(int s, /* connected socket */

char *buf, /* pointer to the buffer */

int n /* number of characters (bytes) we want */

)

{ int bcount; /* counts bytes read */

int br; /* bytes read this pass */

bcount= 0;

br= 0;

while (bcount < n) { /* loop until full buffer */

if ((br= read(s,buf,n-bcount)) > 0) {

bcount += br; /* increment byte counter */

buf += br; /* move buffer ptr for next read */

}

else if (br < 0) /* signal an error to the caller */

return(-1);

}

return(bcount);

}

相同的函数也可以写数据,留给我们的读者吧。

挂起(结束)

和你通过电话和某人交谈后一样,你要在 socket 间关闭连接。一般 close() 函数用来关闭每边的 socket

连接。如果一边的已经关闭,而另外一边却在向他写数据,则返回一个错误代码。

世界语(交流的语言很重要)

现在你可以在机器间联络了,可是要小心你所说的话。许多机器有自己的方言,如 ASCII 和

EBCDIC。更常见的问题是字节顺序问题。除非你一直传输的都是文本,否则你一定要注意这个问题。幸运的是,人

们找出了解决的办法。

在很久以前,人们争论哪种顺序更“正确”。现在必要时有相应的函数来转换。其中有 htons()、ntohs()、

htonl() 和

ntohl()。在传输一个整型数据前,先转换一下。

i= htonl(i);

write_data(s, &i, sizeof(i));

在读数据后,再变回来。

read_data(s, &i, sizeof(i));

i= ntohl(i);

如果你一直坚持这个习惯,你将比别人少出错的机会。

未来在你的掌握了(下一步?)

就用我们刚才讨论的东西,你就可以写自己的通讯程序了。和对待所有的新生事物一样, 最好还是看看别人已经做

了些什么。这里有许多关于 BSD socket

的东西可以参考。

请注意,例子中没有错误检查,这在“真实”的程序中是很重要

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有