1. sockaddr_in , sockaddr , in_addr区别
整理自http://blog.csdn.net/jackychu/article/details/4461927
另一篇相关文章以备参考:struct sockaddr与struct sockaddr_in的区别和联系
1.1 sockaddr:通用的socket地址
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
1.2 sockaddr_in:Internet socket
sockaddr和sockaddr_in可以进行类型转换
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
1.3 in_addr:32位IP地址。
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
};
inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。
1.4 用法
填值的时候使用sockaddr_in结构,而作为函数(如socket, listen, bind等)的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。
通常的用法是:
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
bzero(&(my_addr.sin_zero), 8);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
可以用C++做个不太准确的假设。
sockaddr是base class
sockaddr_in等是derived(派生) class
如此一来,bind, connect, sendto, recvfrom等函数就可以使用base class
来处理多种不同的derived class了。
但是实际上,这是没有继承关系数据结构(C嘛),所以需要强制造型来转换数据类型。正因为如此,在sendto的时候需要给出len长度,因为不同的sockaddr_xx实现长度并不相同。
1.5 P.S.名词解析:
- 主机字节序:
不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。最常见的有两种 1.Little endian:低字节存高地址,高字节存低地址 2.Big endian:低字节存低地址,高字节存高地址 - 网络字节序:
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。 - 字节序转换
为了进行转换bsd socket提供了转换的函数,有下面四个网络与主机字节转换函数:htons ntohs htonl ntohl
s==>short
l==>long
h==>host
n==>network
- htons:把unsigned short类型从主机序转换到网络序
- htonl:把unsigned long类型从主机序转换到网络序
- ntohs:把unsigned short类型从网络序转换到主机序
- ntohl:把unsigned long类型从网络序转换到主机序
在使用little endian的系统中 这些函数会把字节序进行转换
在使用big endian类型的系统中这些函数会定义成空宏
2. gethostbyname()的返回值类型struct hostent
整理自http://blog.csdn.net/wangjie5540/article/details/23429083
2.1 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中的第一地址。
2.2 gethostbyname()的使用
相当简单,你只是传递一个保存机器名的字符串(例如 "whitehouse.gov") 给gethostbyname(),然后从返回的数据结构struct hostent中获取信息。
gethostbyname()成功时返回一个指向结构体hostent的指针,或者是个空(NULL) 指针。(但是和以前不同,不设置errno,h_errno设置错 误信息。请看下面的 herror()。)
如何使用呢? 这个函数可不象它看上去那么难用。
这里是个例子:
int main(int argc, char *argv[]) {
struct hostent *h;
if (argc != 2) { /* 检查命令行 */
fprintf(stderr,"usage: getip address ");
exit(1);
}
if ((h=gethostbyname(argv[1])) == NULL) { /* 取得地址信息 */
herror("gethostbyname");
exit(1);
}
printf("Host name : %s ", h->h_name);
printf("IP Address : %s ",inet_ntoa(*((struct in_addr *)h->h_addr)));
return 0;
}
注意
在使用 gethostbyname() 的时候,你不能用perror() 打印错误信息 (因为 errno 没有使用),你应该调用 herror()。
唯一也许让人不解的是输出 IP 地址信息。h->h_addr 是一个 char *, 但是 inet_ntoa() 需要的是 struct in_addr。因此,我转换 h->h_addr 成 struct in_addr *,然后得到数据。
3. sendto():经socket传送数据
整理自http://blog.csdn.net/tdk_root/article/details/7888574
- 相关函数
send , sendmsg,recv , recvfrom , socket - 表头文件
#include < sys/types.h >
#include < sys/socket.h >
- 定义函数
int sendto ( socket s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ;
- 函数说明
sendto() 用来将数据由指定的socket传给对方主机。
- 参数们:
s:已建好连线的socket,如果利用UDP协议则不需经过连线操作
msg:指向欲连线的数据内容
flags 一般设0,详细描述请参考send()。
to:用来指定欲传送的网络地址,结构sockaddr请参考bind()。
tolen:sockaddr的结构长度。- 返回值:
成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中。- 错误代码
EBADF 参数s非法的socket处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间。
WNOTSOCK 参数 s为一文件描述词,非socket。
EINTR 被信号所中断。
EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断的。
ENOBUFS 系统的缓冲内存不足。
EINVAL 传给系统调用的参数不正确。
- 范例
#include < sys/types.h >
#include < sys/socket.h >
#include
#define PORT 2345 /*使用的port*/
main(){
int sockfd,len;
struct sockaddr_in addr;
char buffer[256];
/*建立socket*/
if(sockfd=socket (AF_INET,SOCK_DGRAM,0))<0){
perror (“socket”);
exit(1);
}
/*填写sockaddr_in 结构*/
bzero ( &addr, sizeof(addr) );
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr=hton1(INADDR_ANY) ;
if (bind(sockfd, &addr, sizeof(addr))<0){
perror(“connect”);
exit(1);
}
while(1){
bzero(buffer,sizeof(buffer));
len = recvfrom(socket,buffer,sizeof(buffer), 0 , &addr &addr_len);
/*显示client端的网络地址*/
printf(“receive from %s\n “ , inet_ntoa( addr.sin_addr));
/*将字串返回给client端*/
sendto(sockfd,buffer,len,0,&addr,addr_len);
}
}
执行 请参考recvfrom()