Chinaunix首页 | 论坛 | 博客
  • 博客访问: 56111
  • 博文数量: 25
  • 博客积分: 2051
  • 博客等级: 大尉
  • 技术积分: 290
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-24 19:56
文章分类
文章存档

2011年(1)

2009年(8)

2008年(16)

我的朋友
最近访客

分类: C/C++

2008-08-23 20:30:27

                         总结

TCP流套接字

0.一些细节

0.1数据结构

通用套接字接口

struct sockaddr 

Uint8_t sa_len
   unsigned short sa_family; /* 地址家族, AF_xxx */ 
   char sa_data[14]; /*14字节协议地址*/ 
 }

sa_family 能够是各种各样的类型, 

sa_data包含套接字中的目标地址和端口信息。

 

 

这好像有点 不明智。  为了处理struct sockaddr,程序员创造了一个并列的结构: struct sockaddr_in ("in" 代表 "Internet")
struct sockaddr_in 

Uint8_t sin_len
   short int sin_family; /* 通信类型 */ 
   unsigned short int sin_port; /* 端口 */ 
   struct in_addr sin_addr; /* Internet 地址 */ 
   unsigned char sin_zero[8]; /* sockaddr结构的长度相同*/ 
}

我们一般只需要定义sin_family, sin_port sin_addr选项。

套接口地址结构有sockaddr_in,sockaddr_in6,sockaddr_ununix套接字)等等

 

 

从程序员的角度来看,通用套接口地址的唯一用途是指向特定与协议的地址结构的指针转换类型,(就是给人转换用的,比如,bind,connect函数中的等等,你用的啥协议,你就用啥套接口)

 

0.2参数传递

套接口地址结构传递给套接口函数时,总是通过指向结构的指针传递,然后在跟一个结构的长度,长度参数是否去地址,取决与传递方向,从进程传内核还是从内核传进程。

1从进程传内核,bind,connect,,sendto

Struct sockaddr_in serv;

Connect(fd,(sa*)&serv,sizeof(serv));

2.从内核传进程accept,getsockname

长度要取地址

int accept(int sockfd, void *addr, int *addrlen); 

0.3 字节排序

大端小端big_endian,little_endian(低字节在地位)如何判断见程序unpv65

一些转换函数hton,ntoh(主要是转换sin_port)

0.4 IP地址处理

旧的那些土函数就都别用了,直接用最好的inet_pton,inet_ntop

 

0.5常用函数

socket()函数 
我想我不能再不提这个了-下面我将讨论一下socket()系统调用。
下面是详细介绍:
#include 
#include 
int socket(int domain, int type, int protocol); 
是它们的参数是什么首先,domain 应该设置成 "AF_INET",就 象上面的数据结构struct sockaddr_in  一样。然后,参数 type 告诉内核  SOCK_STREAM 类型还是 SOCK_DGRAM 类型。最后,把 protocol 设置为  "0"(注意:有很多种 domaintype,我不可能一一列出了,请看 socket()  man帮助。当然,还有一个"更好"的方式去得  protocol。同 时请查阅 getprotobyname()  man 帮助。
socket() 
只是返回你以后在系统调用种可能用到的 socket 描述符,或 者在错误的时候返回-1。全局变量 errno 中将储存返回的错误值。(请参考 perror()  man 帮助。

 

 

bind()函数
  一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联 起来。(如果你想用listen()来侦听一定端口的数据)

 bind() 主题中最后要说的话是,在处理自己的 IP 地址和/或端口的 时候,有些工作是可以自动处理的。
my_addr.sin_port = 0; /* 
随机选择一个没有使用的端口 */ 
  my_addr.sin_addr.s_addr = INADDR_ANY; /* 使用自己的IP地址 */ 
通过将0赋给 my_addr.sin_port,你告诉 bind() 自己选择合适的端 口。同样,将 my_addr.sin_addr.s_addr 设置为 INADDR_ANY,你告诉 它自动填上它所运行的机器的 IP 地址。

 

为了防止出现“address already in use”在调用bind前记得调用setsockopt(listenfd,SOL,SOCKET,SO_REUSEADDR,&on,sizeof(on)

Connect()函数

调用connect将激发tcp3次握手过程。

由于tc内置的connect超时相当长(一般为75秒),所以可以使用SIGALRM为connect设置超时。

具体方法是为这个信号,signal一个函数,在设置一个自己定义的超时时间

 

Listen()函数

2个参数backlog即侦听队列的长度,它的真正含义是:侦听socket 能处理的最大并发连接请求数,其最大取值为128

半连接队列和已连接队列

 

Accept()函数

tcp服务器调用,用于从已完成连接队列对头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠

Accept()函数得处理EINTR和ECONNABORTED(软件引起的连接夭折)的错误,而不能在碰到这些错误时候推出

 

 

断开连接 closeshutdown()

你已经整天都在发送 (send()) 和接收 (recv()) 数据了,现在你准备关 闭你的套接字描述符了。这很简单,你可以使用一般的 Unix 文件描述符  close() 函数:
  close(sockfd);
它将防止套接字上更多的数据的读写。任何在另一端读写套接字的企 图都将返回错误信息。
close
是把描述字的引用技术减1,仅在该技术为0时才关闭他.

所以父进程必须关闭accept以后的套接字

 

如果你想在如何关闭套接字上有多一点的控制,你可以使用函数 shutdown()。它允许你将一定方向上的通讯或者双向的通讯(就象close() )关闭,你可以使用:
int shutdown(int sockfd, int how); 
sockfd 
是你想要关闭的套接字文件描述复。how 的值是下面的其中之 一:
  SHUT_RD

SHUT_WR

SHUT_RDWR
shutdown() 
功时返回 0,失败时返回 -1(同时设置 errno如果在无连接的数据报套接字中使用shutdown() 那么只不过是让 send()  recv() 不能使用(记住你在数据报套接字中使用了 connect  是可以使用它们的)

 

 

 

Getsockname()getpeername()

2个函数参数一样

Getsockname()返回用内核赋予该连接的本地ip地址和本地端口号

函数 getpeername() 告诉你在连接的流式套接字上谁在另外一边。

 

 

gethostname()函数
  甚至比 getpeername() 还简单的函数是 gethostname()。它返回你程 序所运行的机器的主机名字。然后你可以使用 gethostbyname() 以获得你 的机器的 IP 地址。
  下面是定义:
  #include ;
int gethostname(char *hostname, size_t size);
参数很简单:hostname 是一个字符数组指针,它将在函数返回时保存
主机名。sizehostname 数组的字节长度。
函数调用成功时返回 0,失败时返回 -1,并设置 errno

 

struct hostent *gethostbyname(const char *name); 
很明白的是,它返回一个指向 struct hostent 的指针。这个数据结构 是这样的:
   struct hostent {
   char *h_name;
   char **h_aliases;
   int h_addrtype;
   int h_length;
   char **h_addr_list;
   };
   #define h_addr h_addr_list[0] 
这里是这个数据结构的详细资料: 
struct hostent: 
  h_name – 地址的正式名称。
  h_aliases – 空字节-地址的预备名称的指针。
  h_addrtype –地址类型通常是AF_INET 
  h_length – 地址的比特长度。
  h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
  h_addr - h_addr_list中的第一地址。
gethostbyname() 
成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。(但是和以前不同,不设置errnoh_errno 设置错 误信息。请看下面的 herror())




 

阅读(1225) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~