Linux网络编程:TCP网络编程构架

一、字节序

1、不同类型的CPU对大于一个字节的变量的字节存储顺序可能不同。有的系统高位在前,低位在后,称为小端字节序;有的系统低位在前,高位在后,称为大端字节序。而网络传输的数据顺序一定要统一的。所以当内部字节存储顺序和网络字节顺序不同时,就一定要进行转换。

2、如何判断电脑的字节序。

网络字节序采用大端字节顺序。

3、字节序转换函数。

#include <arpa/inet.h>
uint32_t  
htonl(uint32_t hostlong);        /*主机字节序到网络字节序的长整型转换*/
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 <netinet/in.h> 

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 同样大小*/   
};


其中:
struct in_addr   //网络IP的类型

     unsigned long s_addr;
};



三、字符串IP地址和二进制IP地址的转换

1、inet_aton()函数和inet_ntoa()函数

       #include<netddb.h>

     (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 <arpa/inet.h> 
     (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 <netdb.h>
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 <sys/types.h>
#include <sys/socket.h>
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 <sys/types.h>
#include <sys/socket.h>

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 <sys/socket.h>
 int listen(int sockfd, int backlog);  //返回值为-1时,说明监听失败


4、接受客户端连接(accept())//连接服务器(connect())

(1)、当一个客户端的连接请求到达服务器主机侦听的端口时,此时客户端的连接会在队列中等待,直到使用服务器处理接收请求。
 函数accept()成功执行后,会返回一个新的套接字文件描述符来表示客户端的连接,客户端连接的信息可以通过这个新描述符来获得。

(2)、accept()

 #include <sys/types.h>
 #include <sys/socket.h>
 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 <sys/types.h>
 #include <sys/socket.h>
 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);



猜你喜欢

转载自blog.csdn.net/iotflh/article/details/79184149