概述
网络变成首先要注意IP和port的转换,如今电脑基本上是主机字节序,存储依照小端方式,而在网络中传输统一使用大端方式,所以网络变成首先要注意字节序的转换。
一个经常使用的ip转换程序的实现:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define CHIP(ip)
(ip&0xff)<<24 |
(ip&(0xff<<8))<<8 |
(ip&(0xff<<16))>>8|
(ip&(0xff<<24))>>24
int main(int argc,char *argv[])
{
char buf[100]="";
int ip[4]={0};
int oldip,newip;
scanf("%s",buf);
sscanf(buf,"%d.%d.%d.%d",&ip[0],&ip[1],&ip[2],&ip[3]); //格式化输入,注意去地址符号
printf("%d %d %d %dn",ip[0],ip[1],ip[2],ip[3]);
oldip=(ip[3]<<24)|(ip[2]<<16)|(ip[1]<<8)|ip[0];
printf("%xn",oldip);
// newip=(ip[0]<<24)|(ip[1]<<16)|(ip[2]<<8)|ip[3];
newip=CHIP(oldip);//在计算机中依照十六进制存储的
printf("%xn",newip);
//printf("%d %d %d %d n",a[0],a[1],a[2],a[3]);
memset(buf,0,100);
int i;
for(i=0;i<4;i++)//将大端模式的IP转换为十进制 好像有BUG
{
buf[i]=((unsigned int)(newip&((unsigned int)0xff<<8*i))>>8*i);
}
fprintf(stdout,"%d.%d.%d.%dn",buf[3],buf[2],buf[1],buf[0]);
return 0;
}
在网络传输 规定使用大端模式发送。小端模式转大端模式 能够使用这样的宏定义
比如小端模式下十六进制的IP为 64 01 a8 c0 //192.168.1.100
#define CHIP(ip)
(ip&0xff)<<24 |
(ip&(0xff<<8))<<8 |
(ip&(0xff<<16))>>8 |
(ip&(0xff<<24))>>24
转化后的大端模式为:c0 a8 01 64
套接口的概念:
套接口,也叫“套接字”。是操作系统内核中的一个数据结构。它是网络中的节点进行相互通信的门户。它是网络进程的ID。
网络通信,归根究竟还是进程间的通信(不同计算机上的进程间通信)。在网络中。每个节点(计算机或路由)都有一个网络地址。也就是IP地址。
两个进程通信时,首先要确定各自所在的网络节点的网络地址。可是,网络地址仅仅能确定进程所在的计算机,而一台计算机上非常可能同一时候执行着多个进程,所以仅凭网络地址还不能确定究竟是和网络中的哪一个进程进行通信。因此套接口中还须要包含其它的信息。也就是port号(PORT)。在一台计算机中,一个port号一次仅仅能分配给一个进程,也就是说,在一台计算机中,port号和进程之间是一一相应关系。
所以,使用port号和网络地址的组合能够唯一的确定整个网络中的一个网络进程。
port号的概念:
在网络技术中,port大致有两种意思:一是物理意义上的port。如集线器、交换机、路由器等用于连接其它网络设备的接口。
二是指TCP/IP协议中的port,port号的范围从0~65535,一类是由互联网指派名字和号码公司ICANN负责分配给一些经常使用的应用程序固定使用的“周知的port”。其值一般为0~1023.比如http的port号是80,ftp为21。ssh为22。telnet为23等。另一类是用户自定义的,一般是大于1024的整型值。
socket概念
Linux中的网络编程是通过socket接口来进行的。
socket是一种特殊的I/O接口。它也是一种文件描写叙述符。
它是一种经常使用的进程之间通信机制。通过它不仅能实现本地机器上的进程之间的通信。并且通过网络可以在不同机器上的进程之间进行通信。
每个socket都用一个半相关描写叙述{协议、本地地址、本地port}来表示;一个完整的套接字则用一个相关描写叙述{协议、本地地址、本地port、远程地址、远程port}来表示。socket也有一个类似于打开文件的函数调用。该函数返回一个整型的socket描写叙述符,随后的连接建立、传输数据等操作都是通过socket来实现的;
socket类型
(1)流式socket(SOCK_STREAM)用于TCP通信
流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了传输数据的正确性和顺序性。
(2)数据报socket(SOCK_DGRAM)用于UDP通信
数据报套接字定义了一种无连接的服务。数据通过相互独立的报文进行传输,是无序的,而且不保证是可靠、无差错的。它使用数据报协议UDP。
(3)原始socket(SOCK_RAW)用于新的网络协议实现的測试等
原始套接字同意对底层协议如IP或ICMP进行直接訪问,它功能强大但使用较为不便,主要用于一些协议的开发。
头文件<netinet/in.h>
Struct sockddr _in
{
Sa_family_t Sin_family;//存储IPV4格式,见man socket
In_port_t sin_port; //存储端口号
Struct in_addr sin_addr;//IP结构体
};
Struct in_addr
{
In_addr_t s_addr;// 存储IP
};
Typedef unsigned short int sa_family_t;
#define __SOCKADDR_COMMON(sa_prefix)
sa_family_t sa_prefix##family //拼接成sin_family
sa_family:AF_INET IPv4协议 AF_INET6 IPv6协议
经常使用的IP和port转换函数
假设称某个系统所採用的字节序为主机字节序,则它可能是小端模式的,也可能是大端模式的。
而port号和IP地址都是以网络字节序存储的,不是主机字节序,网络字节序都是大端模式。要把主机字节序和网络字节序相互相应起来,须要对这两个字节存储优先顺序进行相互转化。这里用到四个函数:主机转网络序
htons(),ntohs(),htonl()和ntohl().
这四个地址分别实现网络字节序和主机字节序的转化。这里的h代表host,n代表network,s代表short,l代表long。通常16位的IPport号用s代表,而IP地址用l来代表。
#include <arpainet.h>IPv4的函数原型:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *straddr, struct in_addr *addrptr); //点十进制的IP转化为网络字节序。
并保存在在IP结构体
char *inet_ntoa(struct in_addr inaddr);//把网络字节序转化了点十进制IP sockaddr.sin_addr
in_addr_t inet_addr(const char *straddr); //较为经常使用 将十进制数IP转化为sockaddr.sin_addr.s_addr
函数inet_aton():将点分十进制数的IP地址转换成为网络字节序的32位二进制数值。
參数straddr:存放输入的点分十进制数IP地址字符串。
參数addrptr:传出參数,保存网络字节序的32位二进制数值。
函数inet_ntoa():将网络字节序的32位二进制数值转换为点分十进制的IP地址。
函数inet_addr():功能与inet_aton同样,可是结果传递的方式不同。
inet_addr()若成功则返回32位二进制的网络字节序地址。
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
char ip[] = "192.168.0.101";
struct in_addr myaddr;
/* inet_aton */
int iRet = inet_aton(ip, &myaddr);
printf("%xn", myaddr.s_addr);
/* inet_addr */
printf("%xn", inet_addr(ip));
/* inet_pton */
iRet = inet_pton(AF_INET, ip, &myaddr);
printf("%xn", myaddr.s_addr);
myaddr.s_addr = 0xac100ac4;
/* inet_ntoa */
printf("%sn", inet_ntoa(myaddr));
/* inet_ntop */
inet_ntop(AF_INET, &myaddr, ip, 16);
puts(ip);
return 0;
}
名字地址转化
通常。人们在使用过程中都不愿意记忆冗长的IP地址,尤其到Ipv6时,地址长度多达128位,那时就更加不可能一次性记忆那么长的IP地址了。
因此。使用主机名或域名将会是非常好的选择。
主机名与域名的差别:主机名通常在局域网里面使用,通过/etc/hosts文件,主机名能够解析到相应的ip;域名一般是再internet上使用。
域名比如:www.baidu.com
在linux中,有一些函数能够实现主机名和地址的转化。最常见的有gethostbyname()、gethostbyaddr()等。它们都能够实现IPv4和IPv6的地址和主机名之间的转化。
当中gethostbyname()是将主机名转化为IP地址。gethostbyaddr()则是逆操作,是将IP地址转化为主机名。
函数原型:
#include <netdb.h>
struct hostent* gethostbyname(const char* hostname);
struct hostent* gethostbyaddr(const char* addr, size_t len, int family);
结构体:
struct hostent
{
char *h_name; /*正式主机名*/
char **h_aliases; /*主机别名*/
int h_addrtype; /*主机IP地址类型 IPv4为AF_INET*/
int h_length; /*主机IP地址字节长度,对于IPv4是4字节。即32位*/
char **h_addr_list; /*主机的IP地址列表*/
}
#define h_addr h_addr_list[0] /*保存的是ip地址*/
函数gethostbyname():用于将域名(www.baidu.com)或主机名转换为IP地址。參数hostname指向存放域名或主机名的字符串。
函数gethostbyaddr():用于将IP地址转换为域名或主机名。參数addr是一个IP地址,此时这个ip地址不是普通的字符串,而是要通过函数inet_aton()转换。
len为IP地址的长度。AF_INET为4。
family可用AF_INET:Ipv4或AF_INET6:Ipv6。
#include <netdb.h>
#include <sys/socket.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char *ptr, **pptr;
struct hostent *hptr;
char str[32] = {'