一,单进程处理服务器
1,基本概念
想要学习socket网络编程的读者一定要首先学好计算机网络的理论知识,包括
1)osi网络七层模型与ip四层模型
2)套接字含义
3)局域网通信过程
4)广域网通信过程
5)tcp,udp通信协议,在这两个协议中的连接建立,数据封装,传输过程,传输中可能遇到的问题的处理(差错控制,拥塞控制)
6)ip网络层协议,以太网帧协议
7)数据的封装
以上的知识点是个人觉得学习计算机网络必须要理解透彻的一些知识点,读者可以自行学习,这里不过多介绍,接下来要介绍的socket网络编程便是基于以上知识点学习的。下文主要介绍socket编程。
socket网络编程也是系统编程的一种,系统为我们提供了一系列的一些与网络有关的接口,当我们编写一个网络程序时,需要用到网络功能的时候只需要调用这些接口即可。理解透彻了前面所说的七个知识点有助于我们理解这些接口内部具体的实现原理,方便我们在网络编程的时候遇到错误可以更快地调试出正确的运行效果。接下来就介绍这些接口的使用。
2,socket编程
2.1 字节序转换,IP地址转换,套接字赋值
套接字对应程序猿来说就是一套网络通信的接口,使用这套接口就可以完成网络通信。网络通信的主体主要分为两部分:客户端和服务器端。在客户端和服务器通信的时候需要频繁提到三个概念:IP、端口、通信数据,下面先介绍一下需要注意的一些细节问题。
2.1.1 字节序
字节序即我们计算机组成原理曾经学过的一个数据是采用大端存储还是小端存储的问题,下面也可以简单复习一遍:大端模式——数据的高字节保存在内存的低地址中;比如说一个以字节为单位进行寻址的计算机,要存储一个十六进制的数“0x1234”这个数字的话,每一个数据位占据4个存储位,那么没两个数据位就占8个存储位即一个字节,具体存储在硬盘中就是:数据的高字节“12”存储在内存的低地址中,数据的低字节“34”存储在内存的高地址中。也就是说我们要从内存的低地址处开始取数据的话,依次取出来的是一个数据的从高到低的数据位。而小端存储的话:数据的高数据字节存储在高地址位;数据的低数据字节存储在低地址位。
那么问题就来了,对于要通信的多个主机来说,他们内部选择的数据存储方式是不一样的,这也就导致他们读取一串数据的读取方式也是不一样的,甚至在网络中的数据存储方式也是不一样的,(网络字节序一搬是大端)。因此在数据传输的时候必须要对数据进行处理,以保证接收数据的一方使用它自己的主机序接收数据时得到正确的数据。具体的处理方法为下面四个函数。
#include <arpa/inet.h>
// u:unsigned
// 16: 16位, 32:32位
// h: host, 主机字节序
// n: net, 网络字节序
// s: short
// l: int
// 这套api主要用于 网络通信过程中 IP 和 端口 的转换
// 将一个短整形从主机字节序 -> 网络字节序
uint16_t htons(uint16_t hostshort);
// 将一个整形从主机字节序 -> 网络字节序
uint32_t htonl(uint32_t hostlong);
// 将一个短整形从网络字节序 -> 主机字节序
uint16_t ntohs(uint16_t netshort)
// 将一个整形从网络字节序 -> 主机字节序
uint32_t ntohl(uint32_t netlong);
这四个函数一般用来转换ip地址(32位)与端口(16位),例如主机A某一个ip经过 htonl 就可以将A自己的主机端序转化为网络端序,而另一个主机接收到这个数据只需要 ntohl 就可以将网络端序转换为B主机自己的端序,让其正确读取。
注意:一般char型的数据不需要转换,因为本来一个char型变量就会存储一个字节,不论大端小端,他们的数据存储效果都是一样的。
2.1.2 IP地址转换
为了方便我们肉眼读取ip地址,我们一般会把一个32位的ip地址表示成点分十进制的形式,这种形式是方便人类理解了,但是计算机并不理解。因此,如果要在编程中使用点分十进制的形式为一个ip进行初始化的话,一定要记得将这个点分十进制的IP地址转换成一个整型数(网络字节序,下面称呼为大端序),转换的方法如下:
// 主机字节序的IP地址转换为网络字节序
// 主机字节序的IP地址是字符串, 网络字节序IP地址是整形
int inet_pton(int af, const char *src, void *dst);
参数:
af:地址族(IP地址的家族包括ipv4和ipv6)协议
AF_INET: ipv4格式的ip地址
AF_INET6: ipv6格式的ip地址
src:传入参数,对应要转换的点分十进制的ip地址:192.168.1.100
dst:传出参数,函数调用完成,转换得到的大端整形IP被写入到这块内存中
O返回值:成功返回1,失败返回0或者- 1
另外,我们当然也可以将一个整型数转换为一个点分十进制的字符串
#include <arpa/inet.h>
// 将大端的整形数, 转换为小端的点分十进制的IP地址
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
注:还有一组函数也能进程I地址大小端的转换,但是只能处理ipv4的ip地址:文章来源:https://www.toymoban.com/news/detail-616114.html
// 点分十进制IP -> 大端整形
in_addr_t inet_addr (const char *cp);
// 大端整形 -> 点分十进制IP
char* inet_ntoa(struct in_addr in);
2.1.3, 结构体:sockaddr,sockaddr_in
1,sockaddr结构体
系统内核通过读取该结构体当中的内容获取我们要写的套接字的ip地址与端口,比如bind函数就需要输入一个sockaddr的参数来指明其要绑定的ip地址与端口,这个结构体成员如下:文章来源地址https://www.toymoban.com/news/detail-616114.html
typedef unsigned short int sa_family_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
struct sockaddr {
sa_family_t sa_family; // 地址族协议, ipv4
到了这里,关于网络编程(一)TCP单进程服务器编程详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!