【网络】socket——预备知识 | 套接字 | UDP网络通信

这篇具有很好参考价值的文章主要介绍了【网络】socket——预备知识 | 套接字 | UDP网络通信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🐱作者:一只大喵咪1201
🐱专栏:《网络》
🔥格言:你只管努力,剩下的交给时间!
【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp

在前面本喵对网络的整体轮廓做了一个大概的介绍,比如分层,协议等等内容,现在我们直接进入socket(套接字)编程,先来感受到网络编程。

🍚预备知识

🥣源IP地址和目的IP地址

我们知道,在网络通信中,存在两套地址,一套是IP地址,另一套是MAC地址。

  • IP地址:标识计算机在网络中的唯一性。

而IP地址又分为源IP地址和目的IP地址:

  • 源IP地址:标识网络通信发起方。
  • 目的IP地址:标识网络通信的接收方。

在网络通信的报文中,其中报头包含着源IP和目的IP

🥣端口号port

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
如上图所示,报文从用户A的计算机传送到了用户B的计算机,但是网络通信的目的就是将报文从一台计算机传送到另一台计算机吗?

  • 将数据从计算机A传送到计算机B是手段,并不是网络通信的目的。
  • 真正进行通信的是用户A和用户B,也就是计算机A上的某个应用程序和计算机B上的某个应用程序之间在通信

网络通信的目的就是让两台计算机上的两个进程在进行通信。

IP地址可以标识两台计算机的唯一性,但是每台计算机上会存在大量的进程,如何保证计算机A某个进程发送的数据能让计算机B指定的进程接收到呢?

换句话说,如何标识一台计算机上进程的唯一性呢?

采用端口号port来标识计算机上进程的唯一性

  • 端口号是一个2字节16位的整数。
  • 端口号用来标识一个进程,告诉操作系统要把数据交给哪一个进程。
  • 一个端口号只能被一个进程占用。

现在我们有用来标识计算机在网络中唯一性的IP地址,又有用来标识进程在计算机中唯一性的端口号port。

  • 全网唯一进程 = IP地址(全网主机唯一性) + 端口号port(该主机上进程唯一性)
  • socket(套接字) = IP地址 + 端口号port。

所以要想两个进程间实现通信,必须各自有各自的套接字。

网络通信的本质

网络通信实际上是两台计算机或者多台计算机上的进程之间在通信,和我们之前Linux学习的进程间通信的区别在于进程位于不同的计算机上。

  • 网络通信的本质:进程间通信。
  • 要实现进程间通信,必须有共享资源,而网络通信中的网络就是共享资源。
  • 网络通信其实就是在做IO,我们上网的所有行为就两种:a. 把数据发出去。 b. 把数据读回来。
  • Linux下一切皆文件,所以网络在系统中也是一个"文件",也有struct结构体,也有文件描述符。

我们知道,每个进程都有一个pid来标识它在当前计算机上的唯一性,为什么网络中还需要一个端口号port来标识进程的唯一性呢?不能用pid吗?

在技术实现上是完全可以用pid的,所以就需要考虑为什么偏偏就用了端口号port

  • 系统是系统,网络是网络,系统使用pid,网络使用port来标识进程的唯一性,实现了系统与网络的解耦
  • 不是所有进程都提供网络服务或者网络请求的,但是所有的进程都需要pid,只有需要网络的进程才会分配一个port
  • 客户端需要能够直接找到服务器的进程,服务器进程的唯一性不能做任何改变。

比如我们平时使用的QQ,我们手机上的QQ都是客户端,我们打开QQ使用都是在向服务器上的QQ进程发起网络请求,而这个服务器位腾讯公司,服务进程根据用户的网络请求再做出对应的反馈交给用户。

  • 我们下载了某个应用程序以后,该程序里就绑定了服务端对应进程的IP地址和端口号。
  • 所以使用应用程序的时候,就能精准的和服务端上对应的进程进行网络通信。

服务器的IP地址并不会随意变化,为了保证客户端每次都能找到服务端的进程,服务端的port也不能变化。

  • 如果使用pid来代替端口号的话,服务器每重启一次,服务进程的pid值就会改变,客户端就无法找到服务进程了。

绑定了port的进程PCB会被维护在一个哈希表中,port就是key值,操作系统能够根据key值找到对应的PCB,然后再执行它。

🥣认识TCP/UDP协议

这两个协议的具体原理和细节在后面本喵会详细讲解,这里仅需要大概了解一下它两的特定即可。

TCP协议:(Transmission Control Protocol 传输控制协议)。

  • 传输层协议。
  • 需要通信双方建立连接。
  • 是一种可靠传输,不会发生丢包等问题。
  • 面向字节流。

UDP协议:(User Datagram Protocol 用户数据报协议)。

  • 传输层协议。
  • 不需要通信双方建立连接,直接发生即可。
  • 不可靠传输,可能会发生丢包等问题。
  • 描写数据报。

具体这些特点是什么意思,以后会讲解到,这里只需要记住以上内容即可。

🥣网络字节序

我们的计算机分为大端机和小端机,不同的电脑型号就不一样,两台计算机大小端不同,接收到的数据解释出来意义也不同。

  • 规定:网络中的字节序都采用大端

如果你的计算机是大端机,那么就可以直接向网络中发数据和从网络中接收数据,不用做转换。

如果你的计算机是小端机,那么在向网络中发送数据时,需要先将数据转换成大端,再发送到网络中。从网络中接收下来的数据,需要先转换成小端再使用。

此时就存在两个问题:

  • 自己的电脑是大端还是小端?还需要自己去判断一下。
  • 如果自己的电脑是小端,需要自己去将数据转换成大端。

这两个问题虽然我们自己能解决,但是比较繁琐,而且很容易出错,所以操作系统提供了相应的接口来进行大小端转换:

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

#include <arpa/inet.h>//必须包含的头文件
// 主机序列转网络序列
uint32_t htonl(uint32_t hostlong);//将主机上unsigned int类型的数据转换成对应网络字节序
uint16_t htons(uint16_t hostshort);//将主机上unsigned short类型的数据转换成对应网络字节序
// 网络序列转主机序列
uint32_t ntohl(uint32_t netlong);//将从网络中读取的unsigned int类型的数据转换成当前计算机字节序
uint16_t ntohs(uint16_t netshort);//将从网络中读取的unsigned short类型的数据转换成当前计算机字节序
  • 这些函数名很好记,h表示host,代表着主机,n表示network,代表着网络,s表示unit16_t,l表示uint32_t
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回。
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

🍚socket套接字

🥣socket常见系统调用

  1. socket:

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
socket系统调用专门用来创建套接字,在创建的时候指定使用哪种通信协议。

参数解释:

  • int domain

这是地址族,用来指定创建的套接字进行的是网络通信还是本地通信。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
该参数可以填上图所示中的任何一个,经常使用的是AF_INET表示使用IPv4的网络套接字进行网络通信

  • int type

这是用来指定socket提供的能力类型,比如是面向字节流还是面向用户数据报。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
该参数可以使用上图中的任何一个,其中画红色框的是面向字节流和面向用户数据报,也就是TCP和UDP。

  • int protocol

该参数是用来指定具体的协议名的,比如指定TCP或者DUP,但是根据前两个参数就可以确定使用哪个协议了,这个一般设置为0即可。

  • 返回值 int
  • 成功返回一个int类型的值,其实就是一个文件描述符sockfd
  • 失败返回-1,并且设置错误码errno

  1. bind

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
bind用来将IP地址和端口号port创建的socket套接字绑定,也就是将IP地址和端口号port和系统绑定。

参数解释:

  • int sockfd

使用socket()返回的文件描述符sockfd,用来指定绑定哪个套接字。

  • const struct sockaddr * addr

struct sockaddr是一个结构体。

  • socklen_t addrlen

这个参数是表示sockaddr结构体大小的,单位是字节,socklen_t本质是unsigned int类型的32位变量。

  • 返回值int

成功返回0,失败返回-1,并且设置错误码errno


  1. 其他几个接口
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

这几个接口的是TCP协议才会用到,后面本喵再详细讲解。

🥣sockaddr结构体

套接字有很多种类型,常见的有三种:

  • 网络套接字:用户跨主机之间的通信,也能支持本地通信。
  • 原始套接字:可以跨过传输层(TCP/UDP)访问底层的数据。
  • 域间套接字:只能在本地通信。

这些套接字的应用场景完全不同,所以不同种类的套接字就对应一套系统调用接口,所以三套就会对应三套不同的接口。

网络套接字:

struct sockaddr_in {
    short int sin_family;           // 地址族,一般为AF_INET
    unsigned short int sin_port;    // 端口号,网络字节序
    struct in_addr sin_addr;        // IP地址
    unsigned char sin_zero[8];      // 用于填充,使sizeof(sockaddr_in)等于16
};

通过sockaddr_in结构体,将IP地址,端口号,以及网络通信AF_INET通过系统调用bind与系统绑定,从而进行网络通信。

域间套接字:

struct sockaddr_un {
    sa_family_t sun_family;       /* AF_UNIX */
    char sun_path[108];    /* 带有路径的文件名 */
};

sockaddr_un只有域间通信方式AF_UNIX以及域间通信的路径名。


而设计者为了方便使用,无论是网络通信还是域间通信,都使用一套接口,通过设置不同参数来解决所有通信场景。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
sockaddr_insockaddr_un是用于网络通信和域间通信两个不同的通信场景,它们的区别就在于结构体起始处的16位地址类型不同,网络通信使用AF_INET,域间通信使用AF_UNIX

  • 但由于要使用一套接口,所以此时无论哪种通信,都使用sockaddr结构体。
  • 在填充IP地址,端口号,以及地址类型的时候,仍然是对sockaddr_in进行填充。
  • 在使用bind系统调用时,将sockaddr_in强转成sockaddr类型,在函数内部它会根据前两个字节自行判断是什么类型的通信,然后再强制转回去。

可以将sockaddr看成是基类,把sockaddr_insockaddr_un看出是派生类,此时就构成了多态体系。


🍚UDP网络编程

网络通信一定是双方的,一端是服务端(Server)接收数据,另一端是客户端(Client)发送数据。

🥣服务端实现

网络通信首先就是要有套接字,也就是要有IP地址和端口号port,所以我创建的服务器中必须有这两项。

class udpSever
{
private:
	uint16_t _port;//服务进程端口号
	string _ip;//服务器ip地址
	int _sockfd;//socket返回值
};

使用系统调用socket()时,相当于打开了服务器的网卡,在系统中会创建一个struct file类型的结构体,来描述网卡的信息,和文件操作类型,所以会返回一个sockfd,这其实就是一个文件描述符。所以服务器的成员变量有上面代码所示的三个。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp

在构造函数构造服务器对象的时候,需要传入端口号port和IP地址。

  • 端口号port是指定的,一旦指定就不能再更改,这里本喵指定为8080。
  • IP地址是服务器的IP地址。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
shell上使用指令ifconfig,可以看到服务器的IP信息,如上图所示,红色框中的inet:127.0.0.1是本地环回,使用这个IP地址不会经过物理层将数据发出去,而且从链路层又返回到了当前计算机。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp

如上图所示,数据从当前计算机上的客户端进程流向了当前计算机上的服务的进程。所以可以使用本地环回IP地址来进行测试。

但是我们知道,每台计算机在网络中都有一个公网IP,这个IP和本地回环不一样,还有局域网IP等等。存在这么多IP,都标识着服务器这一台计算机。

如果服务器仅仅绑定本地换行的IP地址,当另一台计算机的服务端想要通过公网IP地址发起申请的时候,由于两个IP地址不一样,所以就会忽略客户端的申请。

  • 为了能够让服务端接收所有的网络请求,将服务器的IP地址绑定为0.0.0.0,转换成uint32_t类型就是0。

其实每台计算机绑定的都不止一个IP,当使用bind绑定的IP地址是0.0.0.0的时候,这台计算机收到的所有网络请求都会接收,并且根据相应的端口进行处理。

所以本喵将udpSever中构造函数IP地址形参设为缺省值:

static const string defaultIP = "0.0.0.0";
//构造函数
udpSever(const uint16_t &port, const string &ip = defaultIP)
    : _port(port), _ip(ip), _sockfd(-1)
{}

initSever()

在这个初始化函数中创建socket,并且和操作系统进行绑定bind

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp

  • 使用socket()系统调用创建套接字,并且进行判断,创建失败打印相应错误信息并且退出进程。
  • 填充sockaddr_in结构体:

首先需要对该结构体进行初始化,也就是清0和填充,使用函数bzero进行初始化:

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
这是一个库函数,需要包含头文件<strings.h>,该函数的作用和memset一样,不同之处在于bzero只能清零,第一个参数是目标地址,第二个参数是要清零的字节数。

在填充sockaddr_in结构体的时候,将地址类型sin_family填充为AF_INET表示网络通信。

在填充端口号sin_port的时候,需要使用htons()函数,将主机字节序转换成网络字节序,然后再进行填充。


我们平时看到的IP地址是都是点分十进制的,如127.0.0.1,这是一个字符串,这是为了方便用户阅读,其实在网络中,IP地址是一个uint32_t类型的数据。

所以在填充IP地址地址的时候,需要先将点分十进制的IP地址,使用函数inet_addr()转换成网络类型的uint32_t类型,然后再进行填充。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
形参是const char* cp,也就是点分十进制的IP地址,返回类型是in_addr_t

typedef uint32_t in_addr_t;

其实就是uint32_t类型,只是重命名成了in_addr_t


再使用bind将IP地址和端口号和系统绑定,在绑定之前,sockaddr_in是在栈区上的,操作系统根本不知道设的值是什么,只有使用bind之后,才真正将IP地址和端口号绑定到了操作系统中。

在绑定完成后进行判断,如果失败,打印相应错误信息,并且退出进程。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
这里使用枚举来列举错误码,在创建套接字和绑定的时候,如果发生了错误,在进程退出时给与相关的错误码。

至此,UDP服务器网络通信的预备工作全部完成,此时就可以进行网络通信了。


start()

在预备工作做好以后,还需启动服务器,服务器进程是一个常驻内存的进程,也就是一个while(1)的死循环,在这个循环中进行网络数据的接收,处理,以及发送。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
上图所示的系统调用recvfrom()用来接收网络中发过来的数据,也就是从套接字中接收。

  • 第一个参数是sockfd,是创建套接字时返回的文件描述符fd
  • 第二个参数buf是用来存储从网络中读取下来的数据的。
  • 第三个参数是buf缓冲区的大小。
  • 第四个参数flags是读取的方式,一般设置为0,即阻塞读取数据。
  • 第五个参数sockaddr* src_addr是一个输出型参数,同样传参sockaddr_in结构体,系统会自动对这个结构体进行填充,可以获取数据的来源,包括发送方的地址类型,端口号port以及IP地址。
  • 第六个参数addrlen是第五个输出型结构体变量的大小所在的地址,注意类型是socklen_t*的,和bind的时候不一样。
  • 返回值ssize_t,返回读取到的数据个数,单位是字节,如果读取失败则返回-1。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
如上图所示代码,服务端启动后便常驻内存,网络中有数据便读,没有数据便阻塞等待。

读取数据时,除了数据本身,还有发送方的数据,都在sockaddr_in类型的结构体peer中。

从网络中获取到发送方的IP地址是uint32_t类型的,所以需要使用inet_ntoa()函数将其转换成点分十进制的,方便用户阅读。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
红色框中所示就是inet_ntoa的函数声明,以字符串的形式返回点分十进制的IP地址,参数类型是struct in_addr in

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
in_addr结构体中的变量其实就是一个uint32_t类型的,但是这里传参的时候,不需要具体到s_addr,直接传结构体就行,和绑定IP地址时填充sockaddr_in不一样。

此时我们就可以创建服务器对象并且开始使用他了。


udpSever.cpp

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
在运行可执行程序的时候,需要输入命令行参数,由于IP地址设置了缺省值,所以命令行参数只需要一个端口号即可。

如果执行时,命令行参数没有输对,进程就会退出,并且打印使用信息,如果命令行参数输入正确,就向下执行。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
使用一个智能指针unique_ptr来管理服务器对象,然后再初始化initSever,之后再开始执行服务器进程start

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
此时服务器进程就开始运行了。那么我们怎么知道它是否运行了呢?

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
使用指令sudo netstat -nuap可以查看当前服务器上的网络进程,如上图所示。

  • sudo提权,如果不加,打印的信息不完整。

可以看到,存在一个网络进程,IP地址是0.0.0.0,端口号是8080,进程pid是376,进程名字是udpSever,和我们设定的一样。

至此服务端的工作就做完了,只有客户端发送数据,服务端就可以收到。

🥣客户端实现

class udpClient
{
public:
	//构造函数,初始化服务端IP地址和端口号
	udpClient(const string& severip, const uint16_t& port)
		:_severip(severip),_severport(severport),_sockfd(-1)
	{}
private:
	int _socketfd;//套接字文件描述符
	string _severip;//服务器IP地址
	uint16_t _severport;//服务器端口号
};

对于客户端,只需要知道服务端计算机的IP地址和端口号port即可,不需要知道自己的,在使用构造函数构建客户端对象的时候,需要传入服务端的IP地址和端口号。

  • 客户端有无数个,但是服务端只有一个。

initClient

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
客户端的初始化中,只需要创建套接字即可,不需要显式bind

  • 对于客户端而言,指定唯一的IP地址和端口号没有意义,除了服务器以外,没有其他用户会向客户端发起网络请求。

run()

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
在发送数据之前,需要先告诉操作系统要把数据发送到哪里,所以需要指定服务器的IP地址和端口号port,所以同样需要填充sockaddr_in结构体,创建一个sever变量。

  • 指定地址类型为网络通信,将点分十进制的IP地址转换成uint32_t类型,再将端口号转换成网络字节序。

向网络中发送数据的时候使用到的系统调用是sendto()

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp

  • 第一个参数sockfd是创建的套接字的文件描述符。
  • 第二个参数buf是要发送的数据所在的缓冲区。
  • 第三个参数len是要发生的数据个数,以字节为单位。
  • 第四个参数flags是发送方式,一般设置为0,表示阻塞发送。
  • 第五个参数dest_addr是存放服务器IP地址和端口号port的sockaddr_in结构体变量,在传参的时候需要强转为struct sockaddr*
  • 第六个参数,是第五个参数中结构体变量的大小,以字节为单位。

在调用sendto向网络中发送数据的时候,操作系统会自动将客户端的IP地址和端口号绑定,并且一起发送出去。

  • 由于客户端不需要指定端口号,IP地址也是固定的,所以操作系统会随机给客户端分配一个端口号使用。

在服务端收到客户端的报文中,就包含着客户端的IP地址和端口号,通过从recvfrom填充的sockaddr_in结构体中可以提取客户端操作系统随机分配的那个端口号。

服务端同样可以根据这个IP地址和端口号再给客户端发送消息。

udpClient.cpp

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
在运行udpClient的时候,需要传入两个命令行参数,一个是服务器的IP地址,一个是服务器的端口号port。

同样使用unique_ptr智能指针来管理客户端对象,创建好后进行初始化,然后开始运行。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
由于本喵是在一台服务器上测试客户端和服务端,所以使用的是本地环回,本地环回的IP地址就是127.0.0.1,所以第一个命令行参数就是这个,第二个命令行参数是服务器指定的端口号,前面本喵指定的是8080

  • IP地址使用公网IP,局域网IP,还有本地环回IP,服务端都可以收到客户端的请求,因为服务端绑定的是0.0.0.0

可以看到,在客户端运行起来以后,打印信息socket success: 3,打印的这个数字就是套接字的文件描述符,之所以是3,是因为0,1,2被标准输入输出以及标准错误占用了。

  • 更进一步说明了,套接字创建好后,在操作系统中就是一个文件,也是一个结构体。

此时客户端和服务端都执行起来了,双方就可以进行通信了:

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
运行客户端进程,并且输入要发送的数据内容,如上图所示。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
可以看到,客户端发送的内容,服务端收到了,并且还有客户端的IP地址和端口号。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
重新运行客户端程序,发送数据,可以看到,客户端新收到的数据中,端口号变了,从45839变成了47530,这是因为客户端的端口号是由操作系统分配的,并不是自己指定的,所以每次运行时端口号都不一样。

🍚用户层与网络的解耦

上面我们成功的让客户端和服务端建立起了连接,并且能正常进行网络通信,现在我们让服务端用户层和网络进行解耦。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
在服务端中增加回调函数_callback,使用function包装,回调函数的类型是void(int, string, uint16_t, string)

现在我们实现一个英汉互译的词典,客户端输入英文单词,服务端查它对应的汉语,并且返回给客户端。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp

  • 先在当前路径下创建一个dict.txt文件,里面放入英文和对应的汉语。
  • main函数中,加载词典,调用initDict()函数。
  • 在初始化函数中,使用ifstream类打开dict.txt词典文本,再调用cutString()将文本中的英文和中文分开。
  • 将分开的英文和中文以键值对的形式插入全局变量unordered_map中。

此时我们的词典就建成了,本喵只是简单的写了几个英文单词。

服务器用户层的回调函数:

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
在回调函数中,使用从网络中接收到的客户端请求,也就是英文单词,在unordered_map中查找对应的中文意思,如果找到了,将中文赋值给response_message,如果没有找到,则将unkonwn赋值给response_message

然后将查询的结构response_message通过网络发送给客户端。

  • 可以看到,此时就实现了用户层和网络的解耦,用户要进行的操作完全由用户自己在回调函数中实现即可。
  • 网络底层的配置不用做任何改变。

由于客户端还要接收服务端的反馈信息,所以客户端的网络底层需要做一些修改:

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
在客户端发送完数据以后,便开始等待服务端的反馈,如果服务端发送来信息则接收,并且打印在屏幕上。

这个例子中,打印的就是客户端发送英文单词对应的中文意思。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
客户端程序运行起来后,输入英文单词,如上图所示红色框,就会得到对应的中文,如上图绿色框所示。

  • 翻译的过程是由服务器完成的,客户端只需要将英文单词交给服务器即可。

【网络】socket——预备知识 | 套接字 | UDP网络通信,网络,网络,udp
服务端可以看到客户端发来的英文单词,如上图绿色框中所示。具体的翻译逻辑是在回调函数中实现的。

如此一来我们英汉互译词典的功能就实现了。

还可以做一些相关的改进,此时的客户端,发送数据和读取数据都是阻塞的,在发送完毕后只能等数据到来再去读取,程序不会执行下去。可以将发送和读取弄成两个线程去执行,此时发送和读取就不不干涉了。

🍚总结

UDP协议的网络通信非常简单,只需要使用socket()创建套接字,服务器的话需要显式bind,客户端不需要显式bind,还有就是需要使用recvfrom从网络中(套接字)中读取数据,以及使用sendto向网络中(套接字)中发送数据。文章来源地址https://www.toymoban.com/news/detail-548572.html

到了这里,关于【网络】socket——预备知识 | 套接字 | UDP网络通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux之套接字UDP实现网络通信

    ​ 套接字(Socket)是计算机网络中实现网络通信的一种 编程接口 。它提供了应用程序与网络通信之间的一座桥梁,因为它允许应用程序通过网络发送和接收相应的数据以实现不同主机之间的通信。 ​ 通常套接字由以下两部分组成: 1.网络IP和端口号 :IP用来标识主机,而端口

    2024年02月11日
    浏览(45)
  • 网络编程day1——进程间通信-socket套接字

            基本特征:socket是一种接口技术,被抽象了一种文件操作,可以让同一计算机中的不同进程之间通信,也可以让不同计算机中的进程之间通信(网络通信)         进程A                                                        进程B     创建socket对象

    2024年02月10日
    浏览(89)
  • 「网络编程」第二讲:网络编程socket套接字(三)_ 简单TCP网络通信程序的实现

    「前言」文章是关于网络编程的socket套接字方面的,上一篇是网络编程socket套接字(二),下面开始讲解!  「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 「枫叶先生有点文青病」「每篇一句」 I do not know where to go,but I have been on the road. 我不知

    2024年02月11日
    浏览(52)
  • socket套接字通信 TCP传输控制协议/IP网络协议 5.18

    B/S :浏览器和服务器 C/S :客户机和服务器 网络的层次结构和每层所使用协议的集合 网络采用分层管理的方法,将网络的功能划分为不同的模块 OSI模型: 共7种: 数据的封装与传递过程: 网络传输数据大小user data: 6~1460 网络传输中容易发生拆包和粘包,所以接收和发送的字节

    2024年02月05日
    浏览(78)
  • 【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

    源IP地址(Source IP Address): 源IP地址是数据包发送方(或数据流出发点)的唯一标识符。它用于在互联网或本地网络中定位发送数据包的设备或主机。源IP地址是数据包的出发点,即数据从这个地址开始传送,向目的IP地址指示的设备发送。 在TCP/IP协议中,源IP地址通常由发

    2024年02月14日
    浏览(85)
  • 【探索Linux】P.25(网络编程套接字基本概念 —— 预备知识)

    在上一篇文章中,我们深入探讨了Linux网络的基础知识和它的发展历史,为读者揭开了Linux网络技术演变的序幕。我们了解到,Linux网络技术的发展不仅促进了操作系统本身的成熟,还对整个互联网的进步产生了深远的影响。随着网络技术的不断进步,Linux系统在网络通信方面

    2024年04月27日
    浏览(48)
  • 网络编程『socket套接字 ‖ 简易UDP网络程序』

    🔭个人主页: 北 海 🛜所属专栏: Linux学习之旅、神奇的网络世界 💻操作环境: CentOS 7.6 阿里云远程服务器 在当今数字化时代,网络通信作为连接世界的桥梁,成为计算机科学领域中至关重要的一部分。理解网络编程是每一位程序员必备的技能之一,而掌握套接字编程则

    2024年02月04日
    浏览(57)
  • C++网络编程 TCP套接字基础知识,利用TCP套接字实现客户端-服务端通信

    流式套接字编程针对TCP协议通信,即是面向对象的通信,分为服务端和客户端两部分。 1)加载套接字库( 使用函数WSAStartup() ),创建套接字( 使用socket() ) 2)绑定套接字到一个IP地址和一个端口上( 使用函数bind() ) 3)将套接字设置为监听模式等待连接请求( 使用函数

    2024年02月03日
    浏览(60)
  • 【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序

    我们把数据从A主机发送到B主机,是目的吗?不是,真正通信的不是这两个机器!其实是这两台机器上面的软件(人) 数据有 IP(公网) 标识一台唯一的主机 ,用谁来标识各自主机上客户或者服务进程的唯一性呢? 为了更好的表示一台主机上服务进程的唯一性,我们采用 端口号

    2024年02月12日
    浏览(158)
  • 【网络】socket套接字基础知识

    IP 每台主机都有自己的IP地址,所以当数据从一台主机传输到另一台主机就需要IP地址。报头中就会包含源IP和目的IP 源IP地址:发送数据报那个主机的IP地址,目的IP地址:想发送到的那个主机的IP地址 我们把数据从一台主机传递到另一台主机不是真正目的,真正通信的不是这

    2024年02月03日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包