免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1240 | 回复: 0
打印 上一主题 下一主题

socket学习 和Unix 进程间通讯指南 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-01 12:38 |只看该作者 |倒序浏览
socket学习
struct
unix中一切皆是文件。  文件描述符。对于socket则是socket descriptor,类型是int。两种字节排列顺序。
有两种字节排列顺序:重要的字节在前面(有时叫 "octet"),或者不重要的字节在前面。前一种叫“网络字节顺序 (Network Byte Order)”。
有些机器在内部是按照这个顺序储存数据,而另外一些则不然。当我说某数据必须按照 NBO 顺序,那么你要调用函数(例如 htons() )来将他从本机字节顺序 (Host Byte Order)
转换过来。
如果我没有提到 NBO, 那么就让他是本机字节顺序吧
第一个结构  struct sockaddr. 这个数据结构为许多类型的套接口储存套接口地址信息
struct sockaddr {
        unsigned short    sa_family;    /* address family, AF_xxx       */
        char              sa_data[14];  /* 14 bytes of protocol address */
    };
sa_family 能够是各种各样的事情,但是在这篇文章中是 "AF_INET"。 sa_data 为套接口储存目标地址和端口信息
并列结构
struct sockaddr_in ("in" 代表 "Internet".)
    struct sockaddr_in {
        short int          sin_family;  /* Address family               */
        unsigned short int sin_port;    /* Port number                  */
        struct in_addr     sin_addr;    /* Internet address             */
        unsigned char      sin_zero[8]; /* Same size as struct sockaddr */
    };
注意:对于此结构中的成员sin_zero应该全部置0,这样可以使指向sockaddr_in的指针和指向sockaddr的指针相互转化,sin_family和sockaddr结构中的sa_family一样设置为"AF_INET"。
最后sin_port和sin_addr必须是网络字节顺序。
struct in_addr {
        unsigned long s_addr;
    };
Convert the Natives!
只能转化两种类型 :short (两个字节)和 long (四个字节)。这个函数对于变量类型 unsigned 也适用。
假设你想将 short 从本机字节顺序转换为网络字节顺序。用 "h" 表示 "本机 (host)",接着是 "to",然后用 "n" 表示 "网络 (network)",最后用 "s" 表示 "short": h-to-n-s,
或者 htons() ("Host to Network Short")。
注意:  "h" 表示 "本机 (host)"    接着是 "to"  然后用 "n" 表示 "网络 (network)"   用 "s" 表示 "short"   用“ l”表示“long”
通过以上规则可以在本机字节顺序和网络字节顺序间转化short (两个字节)和 long (四个字节)两种类型。
注意:可移植性 这里是 Unix 世界!记住:在将数据放到网络上的时候,确信他们是网络字节顺序。
为什么在数据结构 struct sockaddr_in 中, sin_addr 和 sin_port 需要转换为网络字节顺序,而 sin_family 不需要呢? 答案是:sin_addr 和 sin_port 分别封装在包的 IP 和 UDP
层。因此,他们必须要是网络字节顺序。但是 sin_family 域只是被内核 (kernel) 使用来决定在数据结构中包含什么类型的地址,所以他应该是本机字节顺序。也即 sin_family 没有
发送到网络上,他们可以是本机字节顺序
IP 地址和如何处理他们
现在我们很幸运,因为我们有很多的函数来方便地操作 IP 地址。没有必要用手工计算他们,也没有必要用
    ina.sin_addr.s_addr = inet_addr("132.241.5.10");
注意:此函数返回的地址已经是按照网络字节顺序的。但是上面的代码不够健壮,需要进行错误检查。
inet_ntoa() ("ntoa" 意思是 "network to ascii"): 此函数可以将struct in_addr,而不是 long类型的参数转化为 numbers-and-dots 格式的ip
socket通讯过程
socket()--得到文件描述符     int socket(int domain, int type, int protocol);  返回以后的系统调用中用到的socket描述符。 错误时返回-1。
bind()--我在哪个端口?   int bind(int sockfd, struct sockaddr *my_addr, int addrlen);将socket和机器上的一定端口绑定。
注意: 如果你想用 listen() 来侦听一定端口的数据,这是必要一步--MUD 经常告诉你说用命令 "telnet x.y.z 6969".)如果你只想用 connect(),那么这个步骤没有必要
connect()--Hello   int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);连接到目标机器
listen()--Will somebody please call me?   int listen(int sockfd, int backlog);
accept()--"Thank you for calling port 3490."   int accept(int sockfd, void *addr, int *addrlen);
系统调用 accept() 会有点古怪的地方的!你可以想象发生这样的事情:有人从很远的地方通过一个你在侦听 (listen()) 的端口连接 (connect()) 到你的机器。
他的连接将加入到等待接受 (accept()) 的队列中。你调用 accept() 告诉他你有空闲的连接。
他将返回一个新的套接口文件描述符!原来的一个还在侦听你的那个端口,新的最后在准备发送 (send()) 和接收 ( recv()) 数据。这就是这个过程!
注意:accept函数返回一个新的socket描述符。用于后面的数据传输。  在系统调用 send() 和 recv() 中你应该使用新的文件描述符
send() and recv()--Talk to me, baby!
int send(int sockfd, const void *msg, int len, int flags);   int recv(int sockfd, void *buf, int len, unsigned int flags);
注意:send() 返回实际发送的数据的字节数--他可能小于你要求发送的数目!
sendto() 和 recvfrom()--Talk to me, DGRAM-style
int sendto(int sockfd, const void *msg, int len, unsigned int flags,
               const struct sockaddr *to, int tolen);
           
  int recvfrom(int sockfd, void *buf, int len, unsigned int flags
                 struct sockaddr *from, int *fromlen);
            
            
注意:如果你是用 connect() 连接一个数据报套接口,你可以简单的调用 send() 和 recv() 来满足你的要求。这个时候依然是数据报套接口,依然使用
UDP,系统自动的加上了目标和源的信息。
close() 和 shutdown()--Get outta my face
其它有用的函数:
getpeername()--Who are you?   int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);函数 getpeername() 告诉你在连接的流式套接口上谁在另外一边
gethostname()--Who am I?   int gethostname(char *hostname, size_t size);           他返回你程序所运行的机器的主机名字。然后你可以使用 gethostbyname()
以获得你的机器的 IP 地址
阻塞 轮询
当你第一次调用 socket() 建立套接口描述符的时候,内核就将他设置为阻塞。如果你不想套接口阻塞,你就要调用函数 fcntl():
通过设置套接口为非阻塞,你能够有效地"询问"套接口以获得信息,但是一般来说轮询不是一个好主意,会浪费cpu时间,更好的方法是
用 select()方法 去查询是否有数据要读进来
select()--多路同步 I/O
select() 让你可以同时监视多个套接口。如果你想知道的话,那么他就会告诉你哪个套接口准备读,哪个又准备好了写,哪个套接口又发生了例外 (exception)。
int select(int numfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
               
               
               

Unix 进程间通讯指南
fork() 基础
fork是unix启动新进程的方法。最基本的,它是这样工作的:父进程(已经存在的那一个) fork() 一个子进程(新的一个)。子进程得到父进程数据的一个
拷贝.
注意:我们必须了解linux下面的一些进程的运行方式。当一个进程死亡时,它并不是完全的消失了。进程终止,它不再运行,但是还有一些残留的小东西等待父进程收回。这些残留的东西
包括子进程的返回值和其他的一些东西。当父进程 fork() 一个子进程后,它必须用 wait() (或者 waitpid())等待子进程退出。正是这个 wait() 动作来让子进程的残留物消失。
但是这个规则也有例外 父进程可以忽略 SIGCLD 软中断而不必要 wait()。
总结:子进程成为 defunct 只到父进程 wait(),除非父进程忽略了 SIGCLD 。更进一步,父进程没有 wait() 就消亡(仍假设父进程没有忽略 SIGCLD )的子进程(活动的或者
defunct)成为 init 的子进程,init 用重手法处理它们。
fork函数需要注意的地方很多可以查看帮助。


      





本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/95597/showart_1915780.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP