Linux Day18 TCP_UDP协议及相关知识

一、网络基础概念

1.1 网络

网络是由若干结点和连接这些结点的链路组成,网络中的结点可以是计算机,交换机、

路由器等设备。

1.2 互联网

把多个网络连接起来就构成了互联网。目前最大的互联网就是因特网。

网络设备有:交换机、路由器、集线器

传输介质有:双绞线、同轴电缆、光纤,无线

00530813c3d64ef5a92822bd3ceacfae.png8266b7137811410998c777e59b748f9a.gif

1.3 IP地址

IP 地址就是给因特网上的每一个主机(或路由器)的每一个接口分配的一个在全世界

范围内唯一的标识符。IP 地址因其特殊的结构使我们可以在因特网上很方便地进行寻址。

IP 地址有分 IPV4 和 IPV6 两种类别格式,IPV4 是类似”A.B.C.D”的格式,它是 32 位的,用“.”分成四个段,每个段是 8 个位(值为 0-255),用 10 进制表示。IPV6 地址是 128 位,格式类似”XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX”,用“:“分成 8 个段,每个段 16 个位,用 4 个 16 进制数表示。

接下来描述的 IP 地址默认都指的是 IPV4 的地址。为了便于寻址,了解目标主机的位置,每个 IP 地址由网络号和主机号两个部分构成。同一个物理网络上所有的主机都使用同 一个网络号,只是主机号不同

1.4 inet_addr()

计算机使用的是无符号整型,而用户熟悉的是点分十进制的字符串。

该函数可以将点分十进制字符串表示的ipv4地址转化为用网络字节序整数表示的ipv4.即无符号整型

1.5 查看ip地址方法

1、Windows

win+r --->cmd-->ipconfig

f93a7b326a984b978c3c0ed4bdb7e210.png6da50f2572344364a15c84e5c8b536a1.gif

2、Linux

ifconfig

1.6 端口号

应用程序的代号,进程号是会变的,但是端口号不会变。所以我们使用端口号去唯一标识应用程序的。

1.7 127.0.0.1

127.0.0.1是回送地址,指本地机,一般用来测试使用。回送地址(127.x.x.x)是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。

通信需要:源ip+端口 目的ip+端口

1.8 协议

网络协议就是一组网络规则的集合,是我们共同遵守的约定或标准。常见的协议:

◼ HTTP:超文本传输协议

◼ FTP: 文件传输协议

◼ TELNET : 是 internet 远程登陆服务的标准协议。

◼ TCP : 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可

靠的、基于字节流的传输层通信协议

◼ UDP :用户数据报协议

◼ IP : Internet Protocol 简称 IP,又译为网际协议或互联网协议

◼ ICMP :因特网控制报文协议

◼ ARP : 地址解析协议,是根据 IP 地址获取 MAC 地址的协议

◼ RARP : 逆地址解析协议

TCP报头

d82d8e3d4206482d96661a5c723206e3.png

这里的ACK,FIN,SYN后面介绍

1.9 服务器端编写

1.9.0 OSI 的 7 层模型与 tcp/ip 协议族体系 4 层结构**

1.9.0.1 OSI/ISO模型

应用层

表示层

会话层

传输层 加TCP/IP协议

网络层 加IP协议

数据链路层 加帧

物理层

1.9.0.2 TCP/IP模型

应用层

表示层

网络层

网际接口层(数据链路层+物理层)

tcp的通信流程80d8fbb4c2044513b03cc430b5a396b7.png

1.9.1 创建套接字

 

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

有了套接字就可以通过网络进行数据的收发

socket()创建套接字,成功返回套接字的文件描述符,失败返回-1

domain: 设置套接字的协议族, AF_INET(IPv4)和 AF_INET6(IPv6)

type: 设置套接字的服务类型 SOCK_STREAM(流服务,用于TCP协议)和SOCK_DGRAM(数据报,用于UDP协议)

注意 PF开头的是协议族,AF开头的是地址族,在Windows上没有任何区别,在Linux上差别不大

4a0cbf9c0be545af8cdae097df07e015.png

protocol: 一般设置为 0,表示使用默认协议

1.9.2 命名socket

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

用来指定套接字使用的IP地址和端口。

bind()将 sockfd 与一个 socket 地址绑定,成功返回 0,失败返回-1

sockfd 是网络套接字描述符,即socket()的返回值。

addr 是地址结构,是一个结构体。

addrlen 是 socket 地址的长度。

 

专用 socket 地址结构

TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用 socket 地址结构体,它们分

别用于 IPV4 和 IPV6:

sin_family: 地址族 AF_INET

sin_port: 端口号,需要用网络字节序表示,在这里我们要注意大小端问题,在不同的操作系统上,大小端是不一样的,为了方便我们的服务器端,我们可以使用htons()函数将所有端口的字节序统一好。

sin_addr: IPV4 地址结构:s_addr 以网络字节序表示 IPV4 地址

 

struct sockaddr_in

{

sa_family_t sin_family;

u_int16_t sin_port;

struct in_addr sin_addr;

};

1.9.3 监听socket

socket被命名后,还不能马上接受客户连接,我们需要使用如下系统调用创建一个监听队列以存放待处理的客户连接。

int listen(int sockfd, int backlog);

listen()创建一个监听队列以存储待处理的客户连接,成功返回 0,失败返回-1

sockfd 是被监听的 socket 套接字

backlog 表示处于完全连接状态的 socket 的上限,典型值为5

1.9.4 接受连接

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

accept()从 listen 监听队列中接收一个连接,成功返回一个新的连接 socket,

该 socket 唯一地标识了被接收的这个连接,失败返回-1

sockfd 是执行过 listen 系统调用的监听 socket

addr 参数用来获取被接受连接的远端 socket 地址,一般是客户端

addrlen 指定该 socket 地址的长度

注意:accept返回值为0表明我们的客户端结束标准输入,这时我们接受到的值就为0.

1.9.5 TCP数据读写

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

ssize_t send(int sockfd, const void *buff, size_t len, int flags);

recv()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大小

send()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长度

flags 参数为数据收发提供了额外的控制,一般为0

1.9.6 关闭连接

关闭该连接对应的socket

int close(int sockfd);

close()关闭一个连接,实际上就是关闭该连接对应的 socket

1.10 客户端编写

1.10.1 创建套接字 socket()

与服务器端一样

1.10.2 发起连接

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

connect()客户端需要通过此系统调用来主动与服务器建立连接,

成功返回 0,失败返回-1

sockfd 参数是由 socket()返回的一个 socket。

serv_addr 是服务器监听的 socket 地址

addrlen 则指定这个地址的长度

1.10.3 发送数据 send()

与服务器端一样

1.10.4 接受数据 recv()

与服务器端一样

1.10.5 结束连接 close()

与服务器端一样

综上所述,TCP协议的基本流程就是如下图所示

a85df326e56d4441ad61cdaf8c530707.png

1.11 编写代码

1.0版本 只能进行一次通信

1.11.1 编写服务端(1.0)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){exit(1);}struct sockaddr_in saddr,caddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//当前主机的ip地址,通过ifconfig获取int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("bind err\n");exit(0);}res=listen(sockfd,5);if(res<0){printf("listen err\n");exit(0);}while(1){int len=sizeof(caddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);if(c<0){continue;}printf("accept c=%d\n",c);char buff[128]={0};int n=recv(c,buff,strlen(buff)-1,0);printf("n=%d\n",n);printf("buff=%s\n",buff);send(c,"ok",2,0);close(c);}
​
}

 

1.11.2 编写客户端(1.0)

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){printf("socket err\n");exit(1);}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//注意服务器和客户端必须要连接在同一个网络下int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("connect err\n");exit(0);}   printf("input:\n");char buff[128]={0};fgets(buff,128,stdin);send(sockfd,buff,strlen(buff)-1,0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("recv:%s\n",buff);close(sockfd);
}

结果

1c941607ee2844389422b3b12fd8265a.png

服务器端可以accept到客户端发送来的数据

客户端可以将服务器端接收到数据后返回的信息接受

二、 TCP 协议特点

2.1 连接的建立与断开

TCP 协议提供的是:面向连接、可靠的、字节流服务。

使用 TCP 协议通信的双发必须先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP 连接是全双工的,双方的数据可以通过一个连接进行读写。完成数据交换之后,通信双方都必须断开连接以释放系统资源。

TCP 的服务器端和客户端编程流程如下:

ece62a83d15e478392d5fa41ffa79c95.png

socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。

bind()方法是用来指定套接字使用的 IP 地址和端口。

listen()方法是用来创建监听队列。监听队列有两种,一个是存放未完成三次握手的连接,一种是存放已完成三次握手的连接。listen()第二个参数就是指定已完成三次握手队列的长度。

accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。每处理一个连接,则accept()返回该连接对应的套接字描述符。如果该队列为空,则 accept 阻塞。

connect()方法一般由客户端程序执行,需要指定连接的服务器端的 IP 地址和端口。该方法执行后,会进行三次握手, 建立连接。

send()方法用来向 TCP 连接的对端发送数据。send()执行成功,只能说明将数据成功写入到发送端的发送缓冲区中,并不能说明数据已经发送到了对端。send()的返回值为实际写入到发送缓冲区中的数据长度。

recv()方法用来接收 TCP 连接的对端发送来的数据。recv()从本端的接收缓冲区中读取数据,如果接收缓冲区中没有数据,则 recv()方法会阻塞。返回值是实际读到的字节数,如果recv()返回值为 0, 说明对方已经关闭了 TCP 连接。

close()方法用来关闭 TCP 连接。此时,会进行四次挥手

2.2 TCP 固定报头

TCP报头267366871c0042688e2ac9d85de44b62.png

ACK:表示确认号是否有效,我们称携带ACK标志的TCP的报文段为确认报文段。

SYN:表示请求建立一个连接,我们称携带SYN标志的TCP的报文段为同步报文段。

FIN:表示通知对方本端要关闭连接了,我们称携带FIN标志的TCP的报文段为结束报文段。

seq(Sequence Number):表示这个tcp包的序列号。

2.3 三次握手

(1)第一次握手,主机A向主机B发出请求数据包:“我想给你发数据,可以吗?”这是第一次对话。

(2)第二次握手,主机B向主机A发送同意连接,并要求同步的数据包(同步就是两台主机协调工作,一台在发送,一台在接收):“可以,你什么时候发?”这是第二次对话。

(3)第三次握手,主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接收吧!”这是第三次对话。

握手发生在connet()的操作下。883dd0247fb7444aa0b4797a1be6d292.png

主机A向主机B发送请求数据包,这是第一次对话,S表示SYN,即请求建立一个连接

主机B向主机A 发送同意连接(回应 ACK,确认该序列号有效),并要求同步数据包(SYN),所以有seq和ACK

主机A向主机B 发送一个数据包确认主机B的要求同步。ACK表示确认该序列号有效7578544f50ef49798bae57b50ce1ec2b.png

 

2.4 中间传输信息9b426271e3424137a81429b0b636831e.png

A主机给B主机发送了一个长度为3的字符串

B主机给A主机发送了一个长度为2的字符串,表明他收到该信息

A主机给B主机发送了确认收到的,即ack

 

2.5 四次挥手

断开连接

四次挥手:

9ce7bdccd8a840648701e439df57307b.png

d0f98f5da01f455e930bda70a39a389e.png

三次挥手:客户端和服务器端关闭相差不大,这个时候就只有三次即中间的ACK和FIN合并在一起。08ce417ae18e4b19b22d70e337147f9f.png

2.6 服务器端 (2.0)

服务器端和客户端可以进行多次交互,直到客户端结束连接

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1){exit(1);}struct sockaddr_in saddr, caddr;memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));if (res == -1){printf("bind err\n");exit(0);}res = listen(sockfd, 5);if (res < 0){printf("listen err\n");exit(0);}while (1){int len = sizeof(caddr);int c = accept(sockfd, (struct sockaddr *)&caddr, &len);if (c < 0){continue;}while (1){printf("accept c=%d\n", c);char buff[128] = {0};int n = recv(c, buff, 127, 0);if (n <= 0)//recv返回值n==0表明客户端已经关闭连接了。{break;}printf("n=%d\n", n);printf("buff=%s\n", buff);send(c, "ok", 2, 0);}sleep(2);close(c);}
}

如果将recv接受数据能力改成每次收取1个,即int n = recv(c, buff, 1, 0);

2.6.1 缓冲区

12b81e0ab3954c48b402b3830ca7fbe4.png

如果recv能力改成1,那么每次只能从缓冲区中读取一个字符,直到接收缓冲区中没有数据,然后才会阻塞

8f8c19d512be4f79bee0a21761fa66f7.png

2.6.2 查看缓冲区

netstat -natp

Recv-Q :接收缓冲区

Send-Q:发送缓冲区

ESTABLLTSHED:已完成三次握手

bbb6ed73f93147e38bfac4c645da63fe.png

此时有2个ok在接收缓冲区,在下一次输出

2.6.3 流失服务特点

5c135299caf541398816c87e68024dd7.png不管中间如何分割,但是最后从发送端到接收端得到的结果是一致的

可靠性:

1:应答确认,超时重传机制

2:去重(报文有序号,有相同序号会丢失重复的),乱序重排(报文有序号)

2.6.4 粘包

粘包发生在发送或接收缓冲区中;应用程序从缓冲区中取数据是整个缓冲区中有多少取多少;那么就有可能第一个数据的尾部和第二个数据的头部同时存在缓冲区,而TCP是流式的,数据无边界,这时发生粘包。

 

4c086824587fd897a5954679fde3e9c0.jpeg

a1a9887df7ca442fb50eb9245dd34c14.png

客户端发了三次数据,服务器端一次接受完,客户端在Recv()处等待服务器回应收到,处于阻塞状态,而服务器端处于第二次recv()所以阻塞,这时这个粘包就会出现问题。解决方法就是在头部加上标识信息。

2.6.5 流量控制

TCP 协议是利用滑动窗口实现流量控制的。一般来说,我们总是希望数据传输得更快一些,不会一次只发一个字节。但是如果发送方把数据发得过快,接受方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来的及接收。

在 TCP 的报头中有一个字段叫做接收通告窗口,这个字段由接收端填充,是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。所以发送端就会有一个发送窗口,这个发送窗口的大小是由接收端填充的接收通告窗口的大小决定的,并且窗口的位置会随着发送端数据的发送和接收到接收端对数据的确认而不断的向右滑动,将之称为滑动窗口。发送方的滑动窗口示意图如下:

f88505d487d84246a6c463a79fc611fc.png

2.7 客户端 (2.0)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){printf("socket err\n");exit(1);}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr= inet_addr("127.0.0.1");int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("connect err\n");exit(0);}   while(1){printf("input:\n");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff)-1,0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("recv:%s\n",buff);}close(sockfd);
}

02f2e5d6d4df4c098d1c90696ba7cc92.png

2.8 多线程,多进程并发

之前的代码存在一个问题:当一个客户端与服务器建立连接以后,服务器端 accept()返回,进而准备循环接收客户端发过来的数据。如果客户端暂时没发数据,服务端会在第 40 行的 recv()阻塞。此时,其他客户端向服务器发起连接后,由于服务器阻塞了,无法执行 accept()接受连接,也就是其他客户端发送的数据,服务器无法读取。服务器也就无法并发同时处理多个客户端。

目的:一个服务器能够同时收到多个客户端的数据

多线程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
​
void * fun(void* arg)
{int *p = (int*)arg;int c = *p;free(p);while( 1 ){char buff[128] = {0};int n = recv(c,buff,1,0);if ( n <= 0)// recv返回值n == 0 说明客户但关闭连接了{break;}
​printf("recv=%s\n",buff);send(c,"ok",2,0);}
​close(c);//关闭连接  挥手printf("client close\n");
}
int main()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);//套接字 文件描述符if ( sockfd == -1 ){exit(1);}
​struct sockaddr_in saddr,caddr;//套接字地址 ip port memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("192.168.84.248");
​int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if ( res == -1 ){printf("bind err\n");exit(1);}
​res = listen(sockfd,5);if ( res == -1 ){exit(1);}
​while( 1 ){int c = accept(sockfd,NULL,NULL);if ( c < 0 ){continue;}
​printf("accept c=%d\n",c);
​pthread_t id;int * p = (int*)malloc(sizeof(c));*p = c;pthread_create(&id,NULL,fun,(void*)p);}
​
}

多进程

使用fork(),父子进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
​
​
void fun(int c)
{while( 1 ){char buff[128] = {0};int n = recv(c,buff,127,0);if ( n <= 0)// recv返回值n == 0 说明客户但关闭连接了{break;}
​printf("recv=%s\n",buff);send(c,"ok",2,0);}
​close(c);//关闭连接  挥手printf("client close\n");
}
int main()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);//套接字 文件描述符if ( sockfd == -1 ){exit(1);}
​struct sockaddr_in saddr,caddr;//套接字地址 ip port memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("192.168.84.248");
​int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if ( res == -1 ){printf("bind err\n");exit(1);}
​res = listen(sockfd,5);if ( res == -1 ){exit(1);}
​while( 1 ){int c = accept(sockfd,NULL,NULL);if ( c < 0 ){continue;}
​printf("accept c=%d\n",c);
​pid_t pid=fork();if(pid<0){printf("fork err\n");close(c);continue;}if(pid==0){close(sockfd);fun(c);exit(0);}}
​
}

2.9 抓包

使用命令

netstat -natp

三次握手时:

7d55ca0c038c498a9d82fceb26144c5b.png

四次挥手时:

ecc2b62f900c45689b18b290107a6e0e.png因为时间原因没有截到 TIME_WAIT

e0b5dd44caa7469d83c274631ccdbe7f.png三、 UDP 协议特点

UDP 数据报服务特点:发送端应用程序每执行一次写操作,UDP 模块就将其封装成一个 UDP 数据报发送。接收端必须及时针对每一个 UDP 数据报执行读操作,否则就会丢包。并且,如果用户没有指定足够的应用程序缓冲区来读取 UDP 数据,则 UDP 数据将被截断。

50008ee78b594a68a89fc710deb95b23.png与TCP不同的是UDP接收到的数据报会直接发给接收端,不会存在缓存区。

(1)UDP是一个非连接的协议,传输数据之前,源端和终端不建立连接,当它想传送时,就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。

在发送端,UDP传送数据的速度仅受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。

(2)由于传输数据不建立连接,因此不需要维护连接状态,包括收发状态等。一台服务机可同时向多个客户机传输相同的消息。

(3)UDP信息包的包头很短,只有8字节,相对于TCP的20字节包头信息,UDP的包头开销很小。

(4)吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。

(5)UDP会尽最大努力去传输和接受数据且没有限制,但并不保证可靠的数据交付,主机也不需要维持复杂的链接状态表(里面有许多参数)。

(6)UDP是面向报文的。发送方的UDP对应用程序传过来的报文,在添加包头后就向下交付给IP层。既不拆分,也不合并,而只是保留这些报文的边界,因此,应用程序需要自己限制合适的报文大小,以免报文太大导致丢失率高。

3.1 API

UDP 数据读写:

recvfrom()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大

src_addr 记录发送端的 socket 地址

addrlen 指定该地址的长度

sendto()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长

dest_addr 指定接收数据端的 socket 地址

addrlen 指定该地址的长度

ssize_t recvfrom(int sockfd, void buff, size_t len, int flags,struct sockaddr src_addr, socklen_t *addrlen);

ssize_t sendto(int sockfd, void buff, size_t len, int flags,struct sockaddr dest_addr, socklen_t addrlen);

3.2 服务器端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(sockfd==-1){printf("socket err\n");exit(1);}struct sockaddr_in saddr,caddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("bind err\n");exit(0);}while(1){int len=sizeof(caddr);char buff[128]={0};recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);printf("buff=%s\n",buff);sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
​
​}close(sockfd);exit(0);
​
}

3.3 客户端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(sockfd==-1){printf("socket err\n");exit(1);}struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");while(1){print("input:\n");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}sendto(sockfd,buff,strlen(buff)-1,0,(struct sockaddr*)&saddr,sizeof(saddr));memset(buff,0,128);recvfrom(sockfd,buff,127,0,NULL,NULL);printf("buff=%s\n",buff);
​}close(sockfd);exit(0);
​
​
}

四、TCP和UDP区别

TCP安全、可靠、面向连接,但是传输速度慢。UDP不安全、不可靠、面向非连接,但是传输速度快。

TCP更适合与文件传输,如果出现错误,会重发。

UDP更适合于实时性,如视频通话,在网卡的情况下,卡住,网好后,能够将实时的画面更新,而不会把之前的再发一遍。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140745.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图层混合算法(一)

常见混合结果展示 图层混合后变暗 正常模式&#xff08;normal&#xff09; 混合色*不透明度&#xff08;100%-混合色不透明度&#xff09; void layerblend_normal(Mat &base,Mat &blend,Mat &dst,float opacity) {if (base.rows ! blend.rows ||base.cols ! b…

测试C#图像文本识别模块Tesseract的基本用法

微信公众号“dotNET跨平台”的文章《c#实现图片文体提取》&#xff08;参考文献3&#xff09;介绍了C#图像文本识别模块Tesseract&#xff0c;后者是tesseract-ocr&#xff08;参考文献2&#xff09; 的C#封装版本&#xff0c;目前版本为5.2&#xff0c;关于Tesseract的详细介绍…

使用Python+Flask/Moco框架/Fiddler搭建简单的接口Mock服务

一、Mock测试 1、介绍 mock&#xff1a;就是对于一些难以构造的对象&#xff0c;使用虚拟的技术来实现测试的过程mock测试&#xff1a;在测试过程中&#xff0c;对于某些不容易构造或者不容易获取的对象&#xff0c;可以用一个虚拟的对象来代替的测试方法接口mock测试&#x…

多维时序 | MATLAB实现WOA-CNN-BiLSTM-Attention多变量时间序列预测(SE注意力机制)

多维时序 | MATLAB实现WOA-CNN-BiLSTM-Attention多变量时间序列预测&#xff08;SE注意力机制&#xff09; 目录 多维时序 | MATLAB实现WOA-CNN-BiLSTM-Attention多变量时间序列预测&#xff08;SE注意力机制&#xff09;预测效果基本描述模型描述程序设计参考资料 预测效果 基…

stc8H驱动并控制三相无刷电机综合项目技术资料综合篇

stc8H驱动并控制三相无刷电机综合项目技术资料综合篇 🌿相关项目介绍《基于stc8H驱动三相无刷电机开源项目技术专题概要》 🔨停机状态,才能进入设置状态,可以设置调速模式,以及转动方向。 ✨所有的功能基本已经完成调试,目前所想到的功能基本已经都添加和实现。引脚利…

SpringSecurity 认证流程

文章目录 前言认证入口&#xff08;过滤器&#xff09;认证管理器认证器说明默认认证器的实现 总结 前言 通过上文了解SpringSecurity核心组件后&#xff0c;就可以进一步了解其认证的实现流程了。 认证入口&#xff08;过滤器&#xff09; 在SpringSecurity中处理认证逻辑是…

CMU15-445 format\clang-format\clang-tidy 失败

CMU15-445 format\clang-format\clang-tidy 失败 问题修改 问题 -- Setting build type to Debug as none was specified. -- Youre using Clang 14.0.0 CMake Warning at CMakeLists.txt:67 (message):BusTub/main couldnt find clang-format.CMake Warning at CMakeLists.tx…

虚幻4学习笔记(15)读档 和存档 的实现

虚幻4学习笔记 读档存档 B站UP谌嘉诚课程&#xff1a;https://www.bilibili.com/video/BV164411Y732 读档 添加UI蓝图 SaveGame_UMG 添加Scroll Box 修改Scrollbar Thickness滚动条厚度 15 15 勾选 is variable 添加text 读档界面 添加背景模糊 添加UI蓝图 SaveGame_Slot …

Rowset Class

本节介绍 This chapter provides an overview of Rowset class and discusses the following topics: Shortcut considerations. Rowset object declaration. Scope of a Rowset object. Rowset class built-in functions. Rowset class methods. Rowset class propertie…

计算机毕业设计 智慧养老中心管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

golang实现远程控制主机

文章目录 ssh原理使用golang远程下发命令使用golang远程传输文件 ssh原理 说到ssh原理个人觉得解释最全的一张图是这张华为画的 Connection establishment 这一步就是建立tcp连接 version negotiation 这一步是ssh客户端(连接者)和被ssh服务端(连接者)进行协议的交换&#xf…

字符函数和字符串函数(1)

前言 C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没有字符串类型的&#xff0c;字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数. 1.求字符串长度 strlen 1.1 strlen size_t strlen ( const char * s…

Windows安装cuda和cudnn教程最新版(2023年9月)

文章目录 cudacudnn cuda 查看电脑的cuda最高驱动版本&#xff08;适用于N卡电脑-Nvidia&#xff09; winR打开命令行&#xff0c;输入nvidia-smi 右上角cuda -version就是目前支持的最高cuda版本&#xff0c;目前是12.2 nvidia官网下载cuda 下载地址&#xff1a;https://d…

华为NFC设置教程(门禁卡/公交卡/校园卡等)

今天把华为NFC设置教程分享给大家 出门带门禁卡、校园卡、银行卡、身份证……东西又多&#xff0c;携带又麻烦&#xff0c;还容易搞丢&#xff0c;有没有一种方法可以把它们都装下&#xff1f;有&#xff01;只要一部手机&#xff0c;出门不带卡包&#xff0c;各种证件&#x…

010_第一代软件开发(二)

第一代软件开发(二) 文章目录 第一代软件开发(二)项目介绍界面布局功能完善快照功能获取可用串口播放按键提示音 关键字&#xff1a; Qt、 Qml、 QSerialPort、 QPixmap、 QSoundEffect 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff…

主打低功耗物联网国产替代,纵行科技ZT1826芯片以速率和灵敏度出圈

在低功耗物联网领域&#xff0c;国产替代的趋势越演越烈。 9月20日&#xff0c;纵行科技在“IOTE 2023深圳物联网通信技术与应用高峰论坛”发表了“自主原创Advanced M-FSK调制技术助力国产替代和泛在物联”的演讲&#xff0c;并推出了ZT1826芯片&#xff0c;以“更低功耗、更…

NetSuite BOM成本查询

这是个23.2的新功能&#xff0c;如题所示是对BOM成本的一个查询工具&#xff0c;是对之前版本那个无用的“Costed Bill of Materials Inquiry”的一次救赎。 其重要的功能是&#xff1a; •基于BOM所使用的版本、工艺路线和成本模板&#xff0c;通过Break Down的方式计算一个装…

超好用的接口自动化框架,lemon-easytest内测版发布,赶紧用起来~

easytest easytest 是一个接口自动化框架。 功能特点&#xff1a; 支持 http 接口测试 支持 json&#xff0c;html,xml 格式的响应断言 支持数据库断言 支持用例标记筛选 支持用例失败重运行 支持多线程 安装 pip install lemon_easytest 快速使用 不需要写任何代码…

整合minio时出现的错误

Action:Correct the classpath of your application so that it contains compatible versions of the classes io.minio.S3Base and okhttp3.RequestBody 这个错误是我在整合minio时报的错&#xff0c;说实话遇到这个错误我还是很头大的&#xff0c;因为之前在springboot项目…