Linux网络编程
基础理论
1、TCP/UDP/端口号
管道、消息队列、共享内存、信号、信号量五种进程间通信属于单机通信。网络编程是由地址和数据组成的。地址是IP地址和端口号组成。数据传输需要遵循协议(HTTP、TCP、UDP)。通过端口号对客户端访问,有FTP、HTTP、SOCKET多种服务。
Linux的socket套接字所用协议最多的是TCP/UDP。
TCP:面向连接(举例:A给B打电话,先拨号再接通);
UDP:面向报文(A给B发信息,不关心是否接通成功);
二者比较:TCP应用于数据传输要求精确的场景;UDP应用于大数据传输;
端口号作用:一台拥有IP地址的主机可以提供许多服务。比如:web、FTP、SMTP(电子邮件传输)服务;实际上,通过”IP地址+端口号“来区分不同服务。
举例:
服务器、TCP/UDP、IP地址、端口号的关系
我是xxx(服务器)
是说汉语(TCP/UDP)
我的楼号(IP地址)是xxxx
我的房间号(端口号)是xxxx
我在等着大家来访,敲门(监听)
客户端
获取服务器IP,再获取服务器端口号连接。
2、字节序
字节序是指多字节在计算机存储或者网络传输各字节的存储顺序。
little endian(小端字节序):将低序字节放在低地址(起始地址)。
Big endian(大端字节序): 将高序字节放在低地址(起始地址)。
举例:
假设需要传输的数据为0x01020304(32位)(前面为高序字节,后面为低序字节)
假设内存地址是 4000 & 4001 & 4002 & 4003
下表为低序 小端字节序
假设内存地址 | 二进制 | 十进制 |
---|---|---|
4003 | 0000 0001 | 01 |
4002 | 0000 0010 | 02 |
4001 | 0000 0011 | 03 |
4000 | 0000 0100 | 04 |
下表为高序 大端字节序
假设内存地址 | 二进制 | 十进制 |
---|---|---|
4003 | 0000 0100 | 04 |
4002 | 0000 0011 | 03 |
4001 | 0000 0010 | 02 |
4000 | 0000 0001 | 01 |
PS:X86序列CPU均为小端字节序;网络字节序均为大端字节序。
获取网络字节序和主机字节序:
//头文件
#include <netinet/in.h>
//函数
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue); //返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue); //返回主机字节序的值
uint32_t ntohl(uint32_t net32bitvalue); //返回主机字节序的值
//h代表host,n代表net,s代表short(两个字节),l代表long(4个字节)。
//通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
//有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取.
提示:以下是本篇文章正文内容,下面案例可供参考
一、socket服务器与客户端的开发步骤
开发步骤:
1、创捷套接字。
2、为套接字添加信息(IP地址和端口号)。
3、监听网络连接。
4、监听到有客服端接入,接受一个连接。
5、数据交互。
6、关闭套接字,断开连接。
示意图:
二、具体使用步骤
1.socket(创建连接协议)
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int socket(int domain, int type, int protocol);
//返回
//success 返回新的套接字的文件描述符
//error 返回 -1
参数
domain:指向所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
AF_INET
代表 IPV4因特网域
AF_INET6
代表 IPV6因特网域
AF_UNIX
代表 UNIX域
AF_ROUTE
代表 路由套接字
AF_KEY
代表 密钥套接字
AF_UNSPEC
代表 未指定
type:指定socket类型
SOCK_STREAM
代表 TCP
SOCK_DGEAM
代表 UDP
SOCK_RAW
代表 IP与ICMP
(对底层协议访问)
protocol:通常取为”0“,”0“对于与type类型的对应默认协议。
IPPROTO_TCP
代表 TCP协议
IPPROTO_UDP
代表 UDP协议
IPPROTO_SCIP
代表 SCIP协议
IPPROTO_TIPC
代表 TIPC协议
2.bind(地址准备好)
功能
用于绑定IP地址和端口号到socketfd。
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
//返回
//success 返回 0
//error 返回 -1
参数
sockfd: socket 返回的文件描述符
addr:struct sockaddr的地址,用于设定要绑定服务端的IP和端口号
addrlen:是addr结构的长度,可以设置成sizeof(struct sockaddr)。
其中:
struct sockaddr {
sa_family_t sa_family; //协议族
char sa_data[14]; //IP+端口号
}
上述结构体参数:
sa_family 用于指定AF_***
表示使用什么协议族的IP。
sa_data 存放IP和端口号。
直接向sa_data中写入IP和端口号有点麻烦,内核提供struct sockaddr_in结构体进行写入,通过**/usr/include/linux/in.h可以看到结构体原型。使用该结构体时需要包含<netinet/in.h>头文件**,且sockaddr_in结构体是专门为tcp/ip协议族使用。
同等替换为:
struct sockaddr_in{
sa_family t sin_family;//协议族
in_port_t sin_port;//端口号
struct in_addr sin_addr;//IP地址结构体
unsigned char sin_zero[8];//填充没有实际意义只是为跟sockadd结构在内存中对序这样两者才能相互转换
}
PS:寻找bind的优化结构步骤和命令
cd /user/include/
grep “struct sockaddr_in{” * -nir
vi linux/in.h +184
其中:n是行号 r是递归
bind步骤中需要用到地址转换API:
//头文件
#include<arpa/inet.h>
//函数:把字符串形式的“192.168.1.123”转为网络能识别的格式 。
int inet_aton(const char* straddr,struct in_addr *addrp);
//函数:把网络格式的ip地址转为字符串形式。
char* inet_ntoa(struct in_addr inaddr);
3.listen(监听)
功能
设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。
主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排邦队的最大连接数。
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int listen(int sockfd, int backlog);
//返回
//success 返回 0
//error 返回 -1
sockfd:是socket系统调用返回的服务器端socket描述符。
backlog:指定在请求队列中允许的最大请求数。
4.accept(连接)
功能
accept函数由TCP服务器调用,用于从已完成连接队列队头返回下个已亮成连接。如果已完成连接队列为空,那么进程被投入睡眠
函数如下:
//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int accept(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
//返回
//success 返回 接受的套接字的描述符
//error 返回 -