Linux应用层开发 --- UDP&TCP

一、UDP协议

UDP协议:User Datagrams Protocal,即用户数据报协议。位于传输层

(一)UDP特点

  1. 无连接

  2. 面向数据报

  3. 尽最大努力交付,不安全不可靠(丢包、乱序)

  4. 机制简单,实时性好,效率高(直播、游戏画面)

(二)网络通信模型

B/S:Browser/Server:浏览器/服务器

  • 通用客户端

  • 数据资源基本都保存在服务端

  • 只需要开发服务端

C/S:Client/Server:客户端/服务端

  • 专用客户端

  • 客户端和服务端都可以保存资源

  • 客户端和服务端都需要开发

(三)UDP通信

1. 客户端

操作流程:

(1)socket():创建socket套接字

套接字:应用层网络通信的端口,本质是文件描述符

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

功能:创建通信的套接字

参数:

domain:使用的协议簇

AF_INET:IPv4(网络层)

AF_INET6:IPv6

type:传输层的协议类型

SOCK_STREAM:TCP

SOCK_DGRAM:UDP

protocol:0 默认方式

返回值:

成功:文件描述符(套接字)

失败:-1

(2)sendto():发送UDP报文

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,

const struct sockaddr *dest_addr, socklen_t addrlen);

功能:发送udp数据报文

参数:

sockfd:套接字

buf:要发送的数据首地址

len:发送的数据字节数

flags:0 按照默认方式发送

dest_addr:服务器的地址信息(IP+PORT)

addrlen:地址信息的长度

返回值:

成功:发送的字节数

失败:-1

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 */

};//新定义的,与struct sockaddr兼容

网络字节序:大端(内存高地址存低字节位,低地址存高字节位)

主机字节序:小端(内存高地址存高字节位,低地址存低字节位)

这个是小端存储

网络字节序和主机字节序的转换

host--->net s:short 2字节 l:long 4字节

htons:2字节数据,主机字节序转网络字节序

htonl:4字节数据,主机字节序转网络字节序

ntohs:2字节数据,网络字节序转主机字节序

ntohl:4字节数据,网络字节序转主机字节序

相关函数

in_addr_t inet_addr(const char *cp);

功能:将点分十进制的字符串IP地址转换成32位的二进制IP地址形式

参数:

cp:字符串IP地址

返回值:整型IP地址

(3)recvfrom():接收UDP报文

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,

struct sockaddr *src_addr, socklen_t *addrlen);

功能:接收UDP数据报文

参数:

sockfd:套接字

buf:存储网络接收到的数据

lrn:期望接收到的字节数

flags:0 默认方式接收

src_addr:保存发送方地址信息的指针

addrlen:地址长度的指针

返回值:

成功:实际接收到的字节数

失败:-1

0:当对方发送0字节数据包

(4)close():关闭套接字

2. 服务端

操作流程

(1)socket():创建socket套接字

(2)bind():绑定服务端的IP和端口号

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能:绑定当前套接字和IP地址及端口号

参数:

sockfd:套接字

addr:要绑定的地址信息指针

addrlen:地址信息的长度

返回值:

成功:0

失败:-1

(3)recvfrom():接收UDP报文

(4)sendto():发送UDP报文

(5)close():关闭套接字

UDP报文头部

源端口号(4字节) 目标端口号(8字节)
数据包长度(2字节) 检验和(Checksum)(2字节)

示例:

client.c

复制代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <string.h>
int main(void)
{
	//socket()
	//sendto();
	//recvfrom();
	//close();
	
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(50000); //接收方的端口号
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.XXX"); //接收方的IP地址

	char buff[1024] = {0};
	while (1)
	{
		fgets(buff, sizeof(buff), stdin);
		ssize_t size = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&seraddr, sizeof(seraddr));
		if (-1 == size)
		{
			perror("sendto error");
			return -1;
		}
		printf("size = %ld\n", size);
		memset(buff, 0, sizeof(buff));
		size = recvfrom(sockfd, buff, sizeof(buff), 0, NULL ,NULL);
		if (size < 0)
		{
			printf("recvfrom error");
			return -1;
		}
		printf("ser-->cli : %s\n", buff);
	}
	close(sockfd);

	return 0;
}

server.c

复制代码
#include "head.h"


int main(void)
{
    //socket()
    //bind()
    //recvfrom()
    //sendto()
    //close()
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket error");
        return -1;
    }
    struct sockaddr_in cliaddr;
    socklen_t clilen = sizeof(cliaddr);

    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(50000);
    seraddr.sin_addr.s_addr = inet_addr("192.168.0.XXX");//接收方IP地址

    int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (ret < 0)
    {
        perror("bind error");
        return -1;
    }
    char buff[1024] = {0};

    while (1)
    {
        memset(buff, 0, sizeof(buff));
        ssize_t size = recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&cliaddr, &clilen);
        if (size < 0)
        {
            perror("recvfrom error");
            return -1;
        }
        printf("[%s : %d][%ld] %s\n",  inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port),size, buff);
        strcat(buff, "----ok!");
        sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&cliaddr, clilen);

    }

    close(sockfd);
    return 0;
}

二、TCP协议

TCP:即传输控制协议(流式套接字),位于传输层

(一)TCP特点

  1. 有连接通信

  2. 面向字节流

  3. 安全可靠的传输协议:三次握手、四次挥手、应答机制、超时重传机制等

  4. 机制复杂,实时性和效率没有UDP高(HTTPS、MQTT、FTP)

(二)TCP的三次握手和四次挥手机制

1. 三次握手

TCP建立连接时,通过三次握手,来确保双方都已经准备就绪。

三次握手由客户端发起

数据收发:

2. 四次挥手

TCP都拿开连接时,通过四次挥手,确保通信双方数据都已经收发结束。

四次挥手可以由客户端发起,也可以由服务端发起

四次挥手机制:

1)应答机制:TCP为发送的数据进行编号,发送数据时,报文头部的序列号是这包数据的第一个数据的编号;将来接收方需要给这包数据发送ACK,ACK报文中确认号是收到的最后一个字节编号+1

2)超时重传机制:TCP每发送一包数据后都要等待应答,如果超时时间之内没有收到应答,则重新发送这包数据

3)滑动窗口机制:缓冲区,保存已发送并收到应答的数据、已发送未收到应答的数据、未发送但在对方处理范围内的数据

4)延迟应答机制:TCP可以发送多组数据,发送的同时等待应答

5)流量控制机制:TCP会根据发送端的数据处理和接收能力调整自己的发送速率,根据ACK中窗口值的大小进行动态调整流量

6)捎带应答机制:ACK可以和应用层发送的数据一起发出,表示对上包数据的响应

标志位

TCP报文头部数据位:

1)SYN:请求建立连接标志位

2)ACK:响应报文标志位

3)PSH:携带数据的报文标志位

4)FIN:请求断开连接标志位

(三)TCP通信

1. 客户端

socket()--->connect()--->send()--->recv()--->close()

(1)connect();

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能:请求建立连接

参数:

sockfd:套接字

addr :服务端的地址

addrlen:地址长度

返回值:

成功:0

失败:-1

(2)recv();

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

功能:接收网络数据

参数:

sockfd:通讯套接字

buf:存放接收到的数据的空间首地址

len:期待接收到的字节数

flags: 0 默认方式

返回值:

成功:返回实际收到的字节数

失败:-1

0:发送断开连接

2. 服务端

socket()--->bind()--->listen()--->accept()--->send()--->close()

(1)listen();

int listen(int sockfd, int backlog);

功能:监听客户端的三次握手

参数:

sockfd:监听套接字

backlog:最多允许监听的客户端的个数

返回值:

成功:0

失败:-1

(2)accept();

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能:接收完成三次握手的客户端,并返回一个通讯套接字

参数:

sockfd:监听套接字

addr:保存接入的客户端的地址信息的指针

addrlen:地址信息长度的指针

返回值:

成功:通讯套接字

失败:-1

示例:

client.c

复制代码
#include "head.h"

int main(void)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(50000);
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.XXX");  //接收方IP地址
	int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (-1 == ret)
	{
		perror("connect error");
		return -1;
	}
	
	send(sockfd, "hello world", 11, 0);

	close(sockfd);
	return 0;
}

server.c

复制代码
#include "head.h"

//socket()
//bind()
//listen()
//accept();
//recv();
//send();
//close()
int main(void)
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("socket error");
		return -1;
	}
	struct sockaddr_in cliaddr;
	socklen_t clilen = sizeof(cliaddr);
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(50000);
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.XXX");  //接收方IP地址

	int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (-1 == ret)
	{
		perror("bind error");
		return -1;
	}
	
	ret = listen(sockfd, 10);
	if (-1 == ret)
	{
		perror("listen error");
		return -1;
	}
	
	int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
	if (-1 == connfd)
	{
		perror("accept error");
		return -1;
	}
	printf("[%s : %d] get online!\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));

	char buff[1024] = {0};
	ssize_t size = recv(connfd, buff, sizeof(buff), 0);
	if (-1 == size)
	{
		perror("recv error");
		return -1;
	}
	printf("buff: %s\n", buff);
	

	close(connfd);
	close(sockfd);

	return 0;
}

(四)TCP报文头部

标志位:

TCP报文头部数据位:

1)SYN:请求建立连接标志位

2)ACK :效应报文标志位

3)PSH:携带数据的报文标志位

4)FIN:请求断开连接标志位

5)URG:紧急数据标志位

6)RST:重置标志位

三、网络抓包工具

wireshark:网络抓包工具,可以抓取经过网卡的网络数据,从而分析网络问题

Linux下安装wireshark

sudo apt-get install wireshark

抓包流程:

(1)启动wireshark:sudo wireshark

(2)选择抓取的网卡:any

(3)选择抓取数据包的过滤条件并触发:udp.port == 50000

(4)进行网络通信

相关推荐
姚不倒17 分钟前
Nginx 虚拟主机:server_name 匹配规则与实战
运维·nginx
念恒123061 小时前
传输层协议TCP
服务器·网络·tcp/ip
代码方舟1 小时前
零信任架构实战:基于天远车辆过户详版查询构建自动化车辆估值网关
运维·人工智能·架构·自动化
爱研究的小梁1 小时前
打破时延‑带宽悖论|乾元通 QMP‑UDP,为无人载具提供低延时高带宽原生通信方案
网络·网络协议·udp
xie0510_1 小时前
TCP socket
linux·网络协议·tcp/ip
aningx1 小时前
# EasyTier 开机自启动部署教程
linux
DeeplyMind2 小时前
Linux 内核模块加载路径与 initramfs 生效机制分析
linux·initramfs·depmod·modprobe
比兔代理2 小时前
代理IP池性能瓶颈在哪?并发、带宽与IP质量的平衡优化策略
服务器·网络·tcp/ip·安全
BlueAsia_Lab2 小时前
Wi‑Fi 认证:企业办理需要准备哪些资料?
运维·服务器·网络