建站基础:wordpress安装教程图解 - 天缘博客,深圳住房保障和建设局官网,电子商务网站建设与管理实训报告,郑州网站建设公司价格二、网络编程基础 1、套接字概述 套接字就是网络编程的ID。网络通信#xff0c;归根到底还是进程间的通信#xff08;不同计算机上的进程间的通信#xff09;。在网络中#xff0c;每一个节点#xff08;计算机或路由器#xff09;都有一个网络地址#xff0c;也就是IP地… 二、网络编程基础 1、套接字概述 套接字就是网络编程的ID。网络通信归根到底还是进程间的通信不同计算机上的进程间的通信。在网络中每一个节点计算机或路由器都有一个网络地址也就是IP地址两个进程通信时首先要确定各自所在网络节点的网络地址。但是网络地址只能确定进程所在的计算机而一台计算机上很可能同时运行着多个进程所以仅凭网络地址还不能确定到底是和网络中哪一个进程通信因此套接口中还需要有其他的信息也就是端口号port。在一台计算机中一个端口号一次只能分配给一个进程也就是说在一台计算机中端口号和进程之间是一一对应的关系。所以使用端口号和网络地址的组合就能唯一确定整个网络中的一个网络进程。 把网络地址和端口号信息放在一个结构体中也就是套接口地址结构大多数的套接口函数都需要一个指向套接口地址结构的指针作为参数并以此来传递地址信息。每个协议族都定义了它自己的套接口地址结构套接字地址结构都以sockaddr_” 开头并以每个协议族名中两个字母作为结尾。 下面是socket所在位置 可以看到套接口有3种类型 1流式套接字SOCK_STREAM 流式套接字提供可靠的、面向连接的通信刘保证数据传输的可靠性和按序收发。TCP通信使用的就是流式套接字。 2数据包套接字SOCK_DGRAM 数据报套接字实现了一种不可靠、无连接的服务。数据通过相互独立的报文进行传输是无序的并且不保证可靠的传输。UDP通信使用的就是数据报套接字。 3原始套接字SOCK_RAW 原始套接字允许对底层协议如IP或ICMP进行直接访问它功能强大但使用较为不便主要用于一些协议的开发。 2、端口号 这里的端口号是逻辑意义上的端口一般是指TCP/IP 协议中的端口端口号的范围为0~65535比如用于浏览网页服务HTTP协议的80端口用于FTP服务的21端口等。其中 0 到1023 一般被系统程序所使用。 那么TCP/IP协议中的端口指的是什么呢举个例子如果IP地址唯一指定了地球上某个地理位置的一间房子端口号就是出入这间房子的门只不过这个房子的门有65536个之多端口是通过端口号来标记的端口号是一个16位的整数范围是从0~65535。 端口号只具有本地意义即端口号只是为了标识本地计算机上的各个进程。在互联网中不同计算机的相同端口号是没有联系的。16bit 的端口号可允许有64K个端口号这个数目对一个计算机来说是足够用的。 3、IP地址 1IP地址的作用 IP地址用来表示网络中的一台主机。准确的说IP地址是一台主机到一个网络的一个连接因为现在一个主机中会有多个网卡。 一个IP地址包含两部分网络号和主机号。其中网络号和主机号根据子网掩码来区分。简单的说有了源IP 和目标 IP数据包就能在不同主机之间传输。 2IP地址格式转换 IP地址有两种不同格式十进制点分形式和32位二进制形式。前者是用户熟悉的形式而后者则是网络传输中IP地址的存储方式。 这里主要介绍IPV4地址转换函数主要有 inet_addr() 、inet_aton() 、inet_ntoa() 。前两者的功能都是将字符串转换成32位网络字节序二进制值第三个将32位网络字节序二进制地址转换成点分十进制的字符串。 inet_addr() 函数语法如下 所需头文件#include arpa/inet.h函数原型int inet_addr(const char *strptr)参数 strptr 要转换的IP地址字符串 函数返回值 成功32位二进制IP地址网络字节序 出错-1 inet_aton() 函数语法如下 所需头文件#include arpa/inet.h函数原型int inet_aton(int family, const char *src , void *drt)参数 family AF_INETIPV4协议 AF_INET6IPV6协议 src要转换的IP地址字符串 函数返回值 成功32位二进制IP地址网络字节序 出错-1 inet_ntoa() 函数语法如下 所需头文件#include arpa/inet.h函数原型int inet_ntoa(int family, const char *src , void *dst, size_t len)参数 family AF_INETIPV4协议 AF_INET6IPV6协议 src要转换的二进制IP地址 dst存放十进制地址字符串的缓冲区 len缓冲区的长度 函数返回值 成功返回dst 出错NULL 4、字节序 字节序又称为主机字节序 Host Byte OrderHBO是指计算机中多字节整型数据的存储方式。字节序有两种大端高位字节存储在低位地址低位字节存储在高位地址和小端和大端序相反PC通常采用小端模式。 为什么需要字节序在网络通信中发送方和接收方有可能使用不同的字节序 为了保证数据接受后能被正确的解析处理统一规定数据以高位字节优先顺序在网络上传输。因此数据在发送前和接收后都需要在主机字节序和网络字节序之间转换。 1函数说明 字节序转换涉及4个函数htons() 、ntohs() 、htonl() 和 ntohl() 。这里的 h 代表 host , n 代表 network , s 代表 short , l 代表 long 。通常 16bit 的IP端口号用前两个函数处理而 IP 地址用后两个函数来转换。调用这些函数只是使其得到相应的字节序用户不需要知道该系统的主机字节序和网络字节序是否真的相等。如果两个相同不需要转换的话该系统的这些函数会定义成空宏。 2函数格式 所需头文件#include netinet/in.h函数原型unit16_t htons(unit 16_t hostshort); unit32_t htonl(unit 32_t hostlong); unit16_t ntohs(unit 16_t netshort); unit32_t ntohl(unit 32_t netlong);函数传入值hostshort主机字节序的16bit 数据 hostlong 主机字节序的32t 数据 netshort 网络字节序的16bit 数据 netlong网络字节序的32bitt 数据函数返回值成功返回转换字节序后的数值 出错-1 5、TCP编程 函数说明 socket()编程的基本函数有socket() 、bind()、listen()、accept()、send()、sendto()、recv()以及recvfrom()等。下面先简单介绍上述函数的功能再结合流程图具体说明 1socket() 该函数用于创建一个套接字同时指定协议和类型。 2bind() 该函数将保存在相应地址结构中的地址信息与套接字进行绑定。它主要用于服务器端客户端创建的套接字可以不绑定地址。 3listen() 在服务端程序成功建立套接字并与地址进行绑定以后通过调用listen() 函数将TCP连接后该函数会返回一个新的已连接套接字。 5connect()客户端通过该函数向服务器端的监听套接字发送连接请求。 6send() 和 recv()这两个函数通常在TCP通信过程中用于发送和接收数据也可用于UDP中。 7sendto()和recvfrom() 这两个函数一般在UDP通信过程中用于发送和接受数据。当用于TCP时后面的几个与地址有关的参数不起作用函数作用等同于 send() 和 recv() 服务器端和客户端使用TCP的流程如下 可以看到通信工作的大致流程如下 1服务器先用socket() 函数来建立一个套接口用这个套接口完成通信的监听及数据的收发 2服务器用bind() 函数来绑定一个端口号和IP地址使套接口与制定的端口号和IP地址相关联 3服务器调用listen()函数使服务器的这个端口和IP处于监听状态等待网络中某一客户机的连接请求。 4客户机调用socket()函数建立一个套接口设定远程IP和端口。 5客户机调用 connect() 函数链接远程计算机指定的端口。 6服务器调用 accept() 函数来接受远程计算机的连接请求建立起与客户机之间的通信连接。 7建立连接以后客户机用write() 函数 或send()函数向socket() 中写入数据也可以用 read() 函数或recv函数读取服务器发送来的数据。 8服务器用 read()函数或recv()函数读取客户机发送来的数据也可以用 write() 函数或send()函数来发送数据。 9完成通信以后使用close()函数关闭socket 连接。 函数格式 1创建套接口 socket() 函数 其语法要点 所需头文件#include sys/socket.h函数原型 int socket(int family, int type,int protocol);函数传入值family协议族 type套接字类型 protocol0原始套接字除外函数返回值成功非负套接字描述符 出错-1 参数family 指明协议族取值如 AF_INETIPv4协议 AF_INET6IPv6协议 AF_LOCALUNIX域协议 AF_ROUTE路由套接字 AF_KEY密钥套接字 这里“AF”代表“Adress Family”(地址族) types指明通信字节流类型其取值如 SOCK_STREAM流式套接字TCP方式 SOCK_DGRAM数据包套接字UDP方式 SOCK_RAM原始套接字 2绑定端口 bind()函数 用socket() 函数创建一个套接口后需要使用bind 函数在这个套接口上绑定一个指定的端口号和IP地址。bind函数原型如下 所需头文件#include sys/socket.h函数原型 int bind(int sockfd, struct sockaddr *my_addr, int addrlen);函数传入值sockfd套接字描述符 my_addr绑定的地址 addrlen地址长度函数返回值成功0 出错-1 这里my_addr是IPv4地址IPv4 套接口地址数据结构以socketaddr_in 命名定义在 netinet/in.h头文件中形式如下 [cpp] view plaincopy struct sockaddr_in sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ ; sin_famliy 为套接字结构协议族如IPv4为AF_INET sin_port 是16位 TCP或UDP端口号网络字节顺序 结构体成员in_addr也是一个结构体定义如下 [cpp] view plaincopy struct in_addr { uint32_t s_addr; /* address in network byte order */ }; 这里s_addr为32位IPv地址网络字节顺序
本地地址可以用INADDR_ANY字节排序函数上面已经介绍下面看一个实例 [cpp] view plaincopy #include stdio.h #include string.h #include stdlib.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #define PORT 2345 int main() { int sockfd; struct sockaddr_in addr; int addr_len sizeof(struct sockaddr_in); if((sockfd socket(AF_INET,SOCK_STREAM,0)) 0) { perror(socket fail); exit(-1); } else { printf(socket created successfully!\nsocket id is %d\n,sockfd); } memset(addr,0,addr_len); addr.sin_family AF_INET; addr.sin_port htons(PORT); addr.sin_addr.s_addr htonl(INADDR_ANY); if(bind(sockfd,(struct sockaddr *)(addr),addr_len) 0) { perror(bind error); exit(-1); } else { printf(bind port successfully!\nlocal port:%d\n,PORT); } return 0; } 执行结果如下 [cpp] view plaincopy fsubuntu:~/qiang/net$ ./socket socket created successfully! socket id is 3 bind port successfully! local port:2345 fsubuntu:~/qiang/net$ 3等待监听函数 所谓监听指的是socket 的端口一直处于等待的状态监听网络中的所有客户机耐心等待某一客户机发送请求。如果客户端有连接请求端口就会接受这个连接。listen 函数用于实现服务器的监听等待功能它的函数原型如下 所需头文件#include sys/socket.h函数原型 int listen(int sockfd, int backlog);函数传入值sockfd套接字描述符 backlog请求队列中允许的最大请求数大多数系统默认值为5函数返回值成功0 出错-1 需要注意的是listen 并未真正的接受连接只是设置socket 的状态为监听模式真正接受客户端连接的是accept 函数
。通常情况下,listen 函数会在 socket ,bind 函数之后调用然后才会调用 accept 函数。 listen函数只适用于SOCK_STREAM或SOCK_SEQPACKET 的socket 类型。如果socket 为 AF_INET 则参数 backlog 最大值可设至128即最多可以同时接受128个客户端的请求。 4接受连接函数 服务器处于监听状态时如果模式可获得客户机的连接请求此时并不是立即处理这个请求而是将这个请求放在等待队列中当系统空闲时再处理客户机的连接请求接受连接请求的函数时accept函数原型如下 所需头文件#include sys/socket.h函数原型 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);函数传入值 sockfd套接字描述符 my_addr用于保存客户端的地址入参 addrlen地址长度 函数返回值 成功建立好连接的套接字描述符 出错-1 当 accept 函数接受一个连接时会返回一个新的 socket 标识符以后的数据传输与读取就是通过这个新的socket 编号来处理原来参数中的 socket 也可以继续使用。接受连接以后远程主机的地址和端口信息会保存在 addr 所指的结构体内。 下面是个实例体验listen 、accept函数的使用 [cpp] view plaincopy #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include sys/socket.h #include unistd.h #include netinet/in.h #include arpa/inet.h #define PORT 2345 int main() { int sockfd,newsockfd; struct sockaddr_in addr,caddr; int addr_len sizeof(struct sockaddr_in); int caddr_len sizeof(struct sockaddr_in); if((sockfd socket(AF_INET,SOCK_STREAM,0)) 0) { perror(socket error); exit(-1); } else { printf(socket successfully!\n); printf(socket id : %d\n,sockfd); } memset(addr,0,addr_len); addr.sin_family AF_INET; addr.sin_port htons(PORT); addr.sin_addr.s_addr htonl(INADDR_ANY); if(bind(sockfd,(struct sockaddr *)addr,addr_len) -1) { perror(bind error); exit(-1); } else { printf(bind successfully!\n); printf(local port : %d\n,PORT); } if(listen(sockfd,5) -1) { perror(listen error); exit(-1); } else { printf(listening...\n); } if((newsockfd accept(sockfd,(struct sockaddr *)caddr,caddr_len)) -1) { perror(accept error); exit(-1); } else { printf(accepted a new connection ..\n); printf(new socket id : %d\n,newsockfd); } return 0; } 执行程序得到输出结果 [cpp] view plaincopy fsubuntu:~/qiang/netprogram/tmp$ ./lisacc socket successfully! socket id : 3 bind successfully! local port : 2345 listening... 程序运行到这停止并一直在这里等待说明本机计算机的 2345号端口正处于监听的状态等待本机上的连接服务请求。此时打开浏览器在浏览器地址栏中输入下列形式的地址 [cpp] view plaincopy http://192.168.3.51:2345/ 这个地址是笔者个人IP地址按ENTER 键这样浏览器会请求连接本地计算机上的2345号端口。此时终端中显示如下结果 [cpp] view plaincopy accepted a new connection .. new socket id : 4 表明程序已经接受了这个连接并创建了一个新的套接口ID为4然后退出了程序。 5、请求连接函数 所谓请求连接是指在客户机向服务器发送信息之前需要先发送一个连接请求请求与服务器建立TCP通信连接。connect 函数可以完成这项功能函数原型如下 所需头文件#include sys/socket.h函数原型 int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);函数传入值 sockfd套接字描述符 sock_addr服务器端地址 addrlen地址长度 函数返回值 成功0 出错-1 这里ser_addr 是一个结构体指针指向一个sockaddr 结构体这个结构体存储着远处服务器的IP与端口号信息。 6、数据读写函数 TCP/UDP读写函数总结注意函数要成对使用 1send函数 建立套接口并完成通信连接以后可以把信息传送到远程主机上这个过程就是信息的发送。而对于远程主机发送来的信息本地主机需要进行接收处理。下面开始讲述这种面向连接的套接口信息发送与接收操作。 用connect 函数连接到远程计算机以后可以用 send 函数将应答信息发送给请求服务的本地主机通信时双向的并且通信的双方是对等的。 send() 函数原型如下 所需头文件#include sys/socket.h函数原型 int send(int sockfd, const void*buf, int len, int flags);函数传入值 sockfd套接字描述符 buf发送缓冲区的地址 入参 len发送数据的长度 flags一般为0 函数返回值 成功实际发送的字节数 出错-1 2recv()函数 函数recv 可以接收远程主机发送来的数据并将这些数据保存到一个数组中函数原型如下 所需头文件#include sys/socket.h函数原型 int recv(int sockfd, const void*buf, int len, int flags);函数传入值 sockfd套接字描述符 buf存放接收数据的缓冲区 出参 len接收数据的长度 flags一般为0 函数返回值 成功实际接收的字节数 出错-1