一、字节序
1、不同类型的CPU对大于一个字节的变量的字节存储顺序可能不同。有的系统高位在前,低位在后,称为小端字节序;有的系统低位在前,高位在后,称为大端字节序。而网络传输的数据顺序一定要统一的。所以当内部字节存储顺序和网络字节顺序不同时,就一定要进行转换。
2、如何判断电脑的字节序。
网络字节序采用大端字节顺序。
3、字节序转换函数。
#include
uint32_t
uint16_t htons(uint16_t hostshort); /*主机字节序到网络字节序的短整型转换*/
uint32_t ntohl(uint32_t netlong); /*网络字节序到主机字节序的长整型转换*/
uint16_t ntohs(uint16_t netshort); /*网络字节序到主机字节序的短整型转换*/
二、Socket套接字
1、Liunx中的网络编程通过Socket(套接字)实现,Socket是一种文件描述符。
2、socket有三种类型:
流式套接字(SOCK_STREAM):TCP协议。
数据报套接字(SOCK_DGRAM):UDP协议。
原始套接字(SOCK_RAW):允许使用IP协议,用于新的网络协议的测试等。
3、套接字的地址结构:
a、 以太网:互联网就是把多个同种类或者不同种类的小网络用路由器连起来。以太网是最为常用的小网络,或者说局域网。
进行套接字编程需要指定套接字的地址作为参数,不同的协议族有不同的地址结构定义方式。
这些地址结构通常以sockaddr_开头,每一个协议族有一个唯一的后缀,以太网协议族的地址结构为sockaddr_in。
b、Socket编程常用的地址结构
在socket程序设计中,struct sockaddr_in用于保存socket地址信息:
#include
struct sockaddr_in
{
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号,网络字节序 */
struct in_addr sin_addr; /* IP地址,网络字节序 */
unsigned char sin_zero[8]; /* 填0 以保持与 struct sockaddr 同样大小*/
};
三、字符串IP地址和二进制IP地址的转换
1、inet_aton()函数和inet_ntoa()函数
#include
(1) int inet_aton(const char *cp,struct in_addr *inp) //点分十进制转化为二进制IP,结 果在struct in_addr *中,转化失败返回0
(2)char *inet_ntoa(struct in_addr in) //二进制转化为点分十进制,结果为char *返回 值
2.inet_pton()函数和inet_ntop()函数
#include
(1)int inet_pton(int af, const char *src, void *dst); //[将"点分十进制" -> "整数"]
这个函数转换字符串到网络地址,第一个参数af是地址族(ipv4为AF_INET),转换后存在dst指向的struct in_addr结构体中。
(2)const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); //将"整数" -> "点分十进制"
这个函数转换网络二进制结构到点分十进制的地址,参数的作用和上面相同,只是多了一个参数socklen_t cnt,他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。
四、IP和主机名之间的转换
在网络上标识一台机器可以用IP,也可以使用主机名。
#include
struct hostent *gethostbyname(const char *hostname)
函数说明:实现主机名(网址(需联网)等)到IP的转换
函数返回值:
struct hostent
{
char *h_name; /* 主机的正式名称*/
char **h_aliases; /* 主机的别名列表,以NULL结尾*/
int h_addrtype; /* 主机的地址类型 AF_INET(ipv4)*/
int h_length; /* 主机的ip地址长度,ipv4为4*/
char **h_addr_list;/*主机的IP号的地址列表(为32位网络地址,而不是字符串),以NULL结尾*/
}
五、TCP网络编程架构
TCP网络编程有两种模式,一种是服务器模式,另一种是客户端模式。
服务器模式创建一个服务程序,等待客户端用户的连接,接收到用户的连接请求后,根据用户的请求进行处理;
客户端模式则根据目的服务器的地址和端口进行连接,向服务器发送请求并对服务器的响应进行数据处理。
1、套接字初始化(socket())
socket()函数介绍
#include
#include
int socket(int domain, int type, int protocol); //如果函数调用成功,会返回一个表示这个 套接字的文件描述符,失败的时候返回–1。
参数:
a.domain即协议域,又称为协议族(family),AF_INET
b.type指定socket类型。常用的socket类型有,SOCK_STREAM、 SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
c.protocol:故名思意,就是指定协议,一般设置为0
2、套接字与端口的绑定(bind())
(1)、在服务器设计中,建立套接字文件描述符成功后,需要对套接字进行地址和端口的绑定,才能进行数据的接收和发送操作。
(2)、bind()函数
#include
#include
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
//将长度为addrlen的struct sockaddr类型的参数my_addr与sockfd绑定在一起,将so
cket绑定到某个端口上
3、设置服务器的侦听连接(listen())
(1)、函数listen()用来初始化服务器可连接队列。
服务器处理客户端连接请求的时候是顺序处理的,同一时间仅能处理一个客户端连接。当多个客户端的连接请求同时到来的时候,服务器并不是同时处理,而是将不能处理的客户端连接请求放到等待队列中,这个队列的长度由listen()函数来定义。
(2)、listen()函数
listen()函数的原型如下,其中的backlog表示等待队列的长度。
#include
int listen(int sockfd, int backlog); //返回值为-1时,说明监听失败
4、接受客户端连接(accept())//连接服务器(connect())
(1)、当一个客户端的连接请求到达服务器主机侦听的端口时,此时客户端的连接会在队列中等待,直到使用服务器处理接收请求。
函数accept()成功执行后,会返回一个新的套接字文件描述符来表示客户端的连接,客户端连接的信息可以通过这个新描述符来获得。
(2)、accept()
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd为服务器的socket描述字
addr为指向struct sockaddr *的指针,用于返回客户端的协议地址
addrlen为指向协议地址长度指针。
结果:
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接;否则返回-1;
注意:
1.accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字,一个服务器通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。
2.而accept函数返回的是已连接的socket描述字。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
(3)、客户端在建立套接字之后,不需要进行地址绑定就可以直接连接服务器。连接服务器的函数为connect(),此函数连接指定参数的服务器,例如IP地址、端口等。
(4)、connect()
connect()函数的原型如下。
#include
#include
int connect(int sockfd, struct sockaddr* addr, int addrlen);
5、接受和发送数据(read()、write())
(1)、当服务器端在接收到一个客户端的连接后,可以通过套接字描述符进行数据的写入操作。对套接字进行写入的形式和过程与普通文件的操作方式一致,内核会根据文件描述符的值来查找所对应的属性,当为套接字的时候,会调用相对应的内核函数。
(2)、write()
int size ;
char data[1024];
size = write(sockfd, data, 1024);
(3)、使用read()函数可以从套接字描述符中读取数据。在读取数据之前,必须建立套接字并连接。
(4)、read()
int size ;
char data[1024];
size = read(s, data, 1024);
6、数据处理及套接字关闭(close())
close(int sockfd);