介绍
字节序介绍:
/*
网络通信时,需要将主机字节序转换成网络字节序(大端),
另外一段获取到数据以后根据情况将网络字节序转换成主机字节序。
// 转换端口
uint16_t htons(uint16_t hostshort); // 主机字节序 - 网络字节序
uint16_t ntohs(uint16_t netshort); // 网络字节序 - 主机字节序
// 转IP
uint32_t htonl(uint32_t hostlong); // 主机字节序 - 网络字节序
uint32_t ntohl(uint32_t netlong); // 网络字节序 - 主机字节序
*/
#include<stdio.h>
#include<arpa/inet.h>
int main()
{
//htons 转换端口
unsigned short a = 0x0102;
printf("a:%x\n",a);
unsigned short b = htons(a);
printf("b:%x\n",b);
printf("====================\n");
//htonl 转换ip
char buf[4]={192,168,1,100};
int num = *(int *)buf;
int sum = htonl(num);
unsigned char *p=(unsigned char *)∑
printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));
printf("====================\n");
//ntohl
unsigned char buf1[4]={1,1,168,192};
int num1=*(int *)buf1;
int sum1=ntohl(num1);
unsigned char *p1=(unsigned char*)&sum1;
printf("%d %d %d %d\n",*p1,*(p1+1),*(p1+2),*(p1+3));
//ntohs
unsigned short a1=0x0201;
printf("a1 : %x\n",a1);
unsigned short b1=ntohs(a1);
printf("b1 : %x\n",b1);
return 0;
}
/*
#include <arpa/inet.h>
// p:点分十进制的IP字符串,n:表示network,网络字节序的整数
int inet_pton(int af, const char *src, void *dst);
af:地址族: AF_INET AF_INET6
src:需要转换的点分十进制的IP字符串
dst:转换后的结果保存在这个里面
// 将网络字节序的整数,转换成点分十进制的IP地址字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
af:地址族: AF_INET AF_INET6
src: 要转换的ip的整数的地址
dst: 转换成IP地址字符串保存的地方
size:第三个参数的大小(数组的大小)
返回值:返回转换后的数据的地址(字符串),和 dst 是一样的
*/
#include<stdio.h>
#include<arpa/inet.h>
int main(){
//创建一个ip字符串,点分十进制的ip地址字符串
char buf[]="192.168.1.4";
unsigned int num=0;
//将点分十进制的IP字符串转换成网络字节序的整数
inet_pton(AF_INET,buf,&num);
unsigned char *p = (unsigned char *)#
printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));
//将网络字节序的ip整数转换成点分十进制的ip字符串
char ip[16]="";
const char * str=inet_ntop(AF_INET,&num,ip,sizeof(ip));
printf("str : %s\n",str);
printf("ip : %s\n",str);
printf("%d\n",ip==str);
return 0;
}
套接字函数
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略
int socket(int domain, int type, int protocol);
- 功能:创建一个套接字
- 参数:
- domain: 协议族
AF_INET : ipv4
AF_INET6 : ipv6
AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)
- type: 通信过程中使用的协议类型
SOCK_STREAM : 流式协议
SOCK_DGRAM : 报式协议
- protocol : 具体的一个协议。一般写0
- SOCK_STREAM : 流式协议默认使用 TCP
- SOCK_DGRAM : 报式协议默认使用 UDP
- 返回值:
- 成功:返回文件描述符,操作的就是内核缓冲区。
- 失败:-1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命
名
- 功能:绑定,将fd 和本地的IP + 端口进行绑定
- 参数:
- sockfd : 通过socket函数得到的文件描述符
- addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息
- addrlen : 第二个参数结构体占的内存大小
int listen(int sockfd, int backlog); // /proc/sys/net/core/somaxconn
- 功能:监听这个socket上的连接
- 参数:
- sockfd : 通过socket()函数得到的文件描述符
- backlog : 未连接的和已经连接的和的最大值, 5
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
- 参数:
- sockfd : 用于监听的文件描述符
- addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
- addrlen : 指定第二个参数的对应的内存大小
- 返回值:
- 成功 :用于通信的文件描述符
- -1 : 失败
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
7. TCP 三次握手
TCP 是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连
接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如 IP 地址、端口号等。
TCP 可以看成是一种字节流,它会处理 IP 层或以下的层的丢包、重复以及错误问题。在连接的建立过程
中,双方需要交换一些连接的参数。这些参数可以放在 TCP 头部。
TCP 提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用 四次挥手
来关闭一个连接。
16 位端口号(port number):告知主机报文段是来自哪里(源端口)以及传给哪个上层协议或
应用程序(目的端口)的。进行 TCP 通信时,客户端通常使用系统自动选择的临时端口号。
32 位序号(sequence number):一次 TCP 通信(从 TCP 连接建立到断开)过程中某一个传输
方向上的字节流的每个字节的编号。假设主机 A 和主机 B 进行 TCP 通信,A 发送给 B 的第一个
TCP 报文段中,序号值被系统初始化为某个随机值 ISN(Initial Sequence Number,初始序号
值)。那么在该传输方向上(从 A 到 B),后续的 TCP 报文段中序号值将被系统设置成 ISN 加上
该报文段所携带数据的第一个字节在整个字节流中的偏移。例如,某个 TCP 报文段传送的数据是字
节流中的第 1025 ~ 2048 字节,那么该报文段的序号值就是 ISN + 1025。另外一个传输方向(从
B 到 A)的 TCP 报文段的序号值也具有相同的含义。
32 位确认号(acknowledgement number):用作对另一方发送来的 TCP 报文段的响应。其值是
收到的 TCP 报文段的序号值 + 标志位长度(SYN,FIN) + 数据长度 。假设主机 A 和主机 B 进行
TCP 通信,那么 A 发送出的 TCP 报文段不仅携带自己的序号,而且包含对 B 发送来的 TCP 报文段
的确认号。反之,B 发送出的 TCP 报文段也同样携带自己的序号和对 A 发送来的报文段的确认序
号。
4 位头部长度(head length):标识该 TCP 头部有多少个 32 bit(4 字节)。因为 4 位最大能表示
15,所以 TCP 头部最长是60 字节。
- 功能: 客户端连接服务器
- 参数:
- sockfd : 用于通信的文件描述符
- addr : 客户端要连接的服务器的地址信息
- addrlen : 第二个参数的内存大小
- 返回值:成功 0, 失败 -1
ssize_t write(int fd, const void *buf, size_t count); // 写数据
ssize_t read(int fd, void *buf, size_t count); // 读数据
TCP socket连接案例
server端
//TCP 通信的服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
//1.创建socket(用于监听的套接字)
int lfd=socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1){
perror("socket");
exit(-1);
}
//2.绑定
struct sockaddr_in saddr;
saddr.sin_family=AF_INET;
saddr.sin_addr.s_addr=INADDR_ANY;
saddr.sin_port=htons(9999);
int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof (saddr));
if(ret==-1){
perror("bind");
exit(-1);
}
//3.监听
ret=listen(lfd,8);
if(ret==-1){
perror("listen");
exit(-1);
}
//4.接收客户端连接
struct sockaddr_in clientaddr;
int len= sizeof(clientaddr);
int cfd = accept(lfd,(struct sockaddr*)&clientaddr,&len);
if(cfd==-1){
perror("accept");
exit(-1);
}
//输出客户端的信息
char clientIP[16];
inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
unsigned short clientPort = ntohs(clientaddr.sin_port);
//5.通信
char recvBuf[1024]={0};
while(1){
//获取客户端的数据
int num= read(cfd,recvBuf,sizeof (recvBuf));
if(num==-1){
perror("read");
exit(-1);
}else if(num>0){
printf("recv client data : %s\n",recvBuf);
}else if(num==0){
printf("client closed...");
break;
}
char *data="hello,i am server";
//给客户端发送数据
write(cfd,data,strlen(data));
}
//关闭文件描述符
close(cfd);
close(lfd);
return 0;
}
client端
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(){
//1.创建套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){
perror("socket");
exit(-1);
}
//2.连接服务器端
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
inet_pton(AF_INET,"192.168.108.128",&serveraddr.sin_addr);
serveraddr.sin_port=htons(9999);
int ret=connect(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(ret==-1){
perror("connect");
exit(-1);
}
//3.通信
char recvBuf[1024]={0};
while(1){
char *data="hello,i am client";
//给客户端发送数据
write(fd,data,strlen(data));
sleep(1);
int len=read(fd,recvBuf,sizeof(recvBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv server data : %s\n",recvBuf);
}else if(len==0){
//表示服务器端断开连接
printf("server closed");
break;
}
}
close(fd);
return 0;
}