【Linux网络编程入门】Day5_socket编程基础

news2024/11/14 1:21:58

socket 编程基础

Linux 下的网络编程:socket 编程;

socket是内核向应用层提供的一套网络编程接口,用户基于 socket 接口可开发自己的网络相关应用程序

⚫ socket 简介

⚫ socket 编程 API 介绍

⚫ socket 编程实战

socket 简介


​ 套接字是 Linux 下的一种进程间通信机制(socket IPC),当然也可以是同一台主机上的不同应用程序;socket IPC 通常使用客户端<—>服务器这种模式完成通信,多个客户端可以同时连接到服务器中,与服务器之间完成数据交互。

​ 内核向应用层提供了 socket 接口,对于应用程序开发人员来说,我们只需要调用 socket 接口开发自己的应用程序即可!socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口。在设计模式中,socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议隐藏在 socket 接口后面,对用户来说,一组简单的接口就是全部,让 socket 去组织数据,以符合指定的协议。所以,我们无需深入的去理解 tcp/udp 等各种复杂的 TCP/IP 协议,socket 已经为我们封装好了,我们只需要遵循 socket 的规定去编程,写出的程序自然遵循 tcp/udp 标准的。

​ 当前网络中的主流程序设计都使用 socket 进行编程,因为它简单易用,它还是个标准(BSD socket),能在不同平台很方便移植,比如你的一个应用程序是基于 socket 接口编写的,那么它可以移植到任何实现 BSD socket 标准的平台,譬如 LwIP,它兼容 BSD Socket;又如 Windows,它也实现一套基于socket 的套接字接口,更甚至在国产操作系统中,如 RT-Thread,它也实现了 BSD socket 标准的 socket 口。

socket 编程接口介绍


介绍socket 编程中使用到的一些接口函数

使用 socket 接口需要在应用程序代码中包含两个头文件

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

为了能够正常让客户端能正常连接到服务器,服务器必须遵循以下处理流程:

①调用 socket()函数打开套接字;
②调用 bind()函数将套接字与一个端口号以及 IP 地址进行绑定;
③调用 listen()函数让服务器进程进入监听状态,监听客户端的连接请求;
④调用 accept()函数处理到来的连接请求。
socket()函数
  • socket()函数类似于 open()函数,它用于创建一个网络通信端点(打开一个网络通信);

  • 如果成功则返回一个网络文件描述符,通常把这个文件描述符称为 socket 描述符(socket descriptor);

  • socket 描述符跟文件描述符一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作

  • 当不再需要该文件描述符时,可调用close()函数来关闭套接字,释放相应的资源

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

int socket(int domain, int type, int protocol);
/*
调用成功:
	socket()返回文件描述符一般称为 socket 描述符。
调用失败:
	返回-1,并且会设置 errno 变量以指示错误类型。*/
参数

domain

  • ​ 参数 domain 用于指定一个通信域;这将选择将用于通信的协议族

  • ​ 对于 TCP/IP 协议来说,通常选择 AF_INET 就可以了;

  • ​ 如果你的 IP 协议的版本支持 IPv6,那么可以选择 AF_INET6。

可选的协议族如下表所示:

image-20240814035651097

type

  • ​ 参数 type 指定套接字的类型,当前支持的类型有:

    image-20240814035906988

protocol

  • ​ 参数 protocol 通常设置为 0,表示为给定的通信域和套接字类型选择默认协议

  • ​ 当对同一域和套接字类型支持多个协议时,可以使用 protocol 参数选择一个特定协议。

    在 AF_INET 通信域中:
    	套接字类型为SOCK_STREAM 的默认协议是传输控制协议(Transmission Control Protocol,TCP 协议)。
    在 AF_INET 通信域中:
    	套接字类型为 SOCK_DGRAM 的默认协议时 UDP。**使用示例**
    

典例

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//打开套接字
if (0 > socket_fd) 
{
     perror("socket error");
     exit(-1);
}
......
......
close(socket_fd); //关闭套接字
bind()函数
  • 用于将一个 IP 地址或端口号与一个套接字进行绑定(将套接字与地址进行关联);
原型
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
成功:
	返回 0;
失败:
	返回-1,并设置 errno 以提示错误原因。
*/
参数
  • sockfd、 addr:将参数 sockfd 指定的套接字与一个地址 addr 进行绑定;

  • addrlen:addrlen指定了addr所指向的结构体对应的字节长度

image-20240814040911890

典例

struct sockaddr_in socket_addr;
memset(&socket_addr, 0x0, sizeof(socket_addr)); //清零

//填充变量
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socket_addr.sin_port = htons(5555);

//将地址与套接字进行关联、绑定
bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(socket_addr));

注意:

	代码中的 htons 和 htonl 并不是函数,只是一个宏定义,主要的作用在于为了避免大小端的问题,需要这些宏需要在我们的应用程序代码中包含头文件<netinet/in.h>。

Tips:

	bind()函数并不是总是需要调用的,只有用户进程想与一个具体的 IP 地址或端口号相关联的时候才需要调用这个函数。
	如果用户进程没有这个必要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,通常在客户端应用程序中会这样做。
listen()函数
  • ​ listen()函数只能在服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求

  • ​ listen()函数在一般在 bind()函数之后调用,在 accept()函数之前调用;

  • ​ 无法在已连接的套接字(即已成功执行 connect()的套接字或由 accept()返回的套接字)执行listen()

原型
int listen(int sockfd, int backlog);
/*当一个客户端的连接请求到达并且该队列为满时,客户端收到一个表示连接失败的错误,本次请求丢弃不作处理。*/
参数

backlog :用来描述 sockfd 的等待连接队列能够达到的最大值

注意

服务器进程正处理客户端连接请求时,存在其它的客户端请求建立连接:

	在服务器进程正处理客户端连接请求的时候,可能还存在其它的客户端请求建立连接,因为 TCP 连接是一个过程,由于同时尝试连接的用户过多,使得服务器进程无法快速地完成所有的连接请求,那怎么办呢?
	直接丢掉其他客户端的连接肯定不是一个很好的解决方法。
	因此内核会在自己的进程空间里维护一个队列,这些连接请求就会被放入一个队列中,服务器进程会按照先来后到的顺序去处理这些连接请求,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限,这个 backlog 参数告诉内核使用这个数值作为队列的上限。
	而当一个客户端的连接请求到达并且该队列为满时,客户端可能会收到一个表示连接失败的错误,本次请求会被丢弃不作处理。
accept()函数
  • ​ 服务器调用 listen()函数之后,就会进入到监听状态,等待客户端的连接请求;

  • ​ 使用 accept()函数获取客户端的连接请求并建立连接

原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*如果 accept()函数执行出错,将会返回-1,并会设置 errno 以指示错误原因。*/
参数
addr:
	一个传出参数,参数 addr 用来返回已连接的客户端的 IP 地址与端口号等这些信息。
addrlen:
	应设置为 addr 所指向的对象的字节长度,如果我们对客户端的 IP 地址与端口号这些信息不感兴趣,可以把 arrd 和 addrlen 均置为空指针 NULL。

注意

为了能够正常让客户端能正常连接到服务器,服务器必须遵循以下处理流程:

①、调用 socket()函数打开套接字;

②、调用 bind()函数将套接字与一个端口号以及 IP 地址进行绑定;

③、调用 listen()函数让服务器进程进入监听状态,监听客户端的连接请求;

④、调用 accept()函数处理到来的连接请求。

	accept()函数通常只用于服务器应用程序中,如果调用 accept()函数时,并没有客户端请求连接(等待连
接队列中也没有等待连接的请求),此时 accept()会进入阻塞状态,直到有客户端连接请求到达为止。
	当有客户端连接请求到达时,accept()函数与远程客户端之间建立连接,accept()函数返回一个新的套接字。	这个套接字与 socket()函数返回的套接字并不同,socket()函数返回的是服务器的套接字(以服务器为例),而accept()函数返回的套接字连接到调用 connect()的客户端,服务器通过该套接字与客户端进行数据交互,譬如向客户端发送数据、或从客户端接收数据。
	所以,理解 accept()函数的关键点在于它会创建一个新的套接字,其实这个新的套接字就是与执行connect()(客户端调用 connect()向服务器发起连接请求)的客户端之间建立了连接,这个套接字代表了服务器与客户端的一个连接。
connect()函数
  • ​ 用于客户端应用程序中,客户端调用 connect()函数将套接字 sockfd 与远程服务器进行连接

  • 客户端通过 connect()函数请求与服务器建立连接

     对于 TCP 连接来说:
    		调用该函数将发生 TCP 连接的握手过程,并最终建立一个 TCP 连接;
     对于 UDP 协议来说:
            调用这个函数只是在 sockfd 中记录服务器IP 地址与端口号,而不发送任何数据。
    
原型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*函数调用成功则返回 0,失败返回-1,并设置 errno 以指示错误原因。*/
参数

addr : 指定了待连接的服务器IP 地址以及端口号等信息;

addrlen :指定了 addr 指向的 struct sockaddr对象的字节大小

发送和接收函数
  • 一旦客户端与服务器建立好连接之后,我们就可以通过套接字描述符来收发数据

  • 对于客户端使用socket()返回的套接字描述符,而对于服务器,需要使用 accept()返回的套接字描述符;

  • 与读写普通文件是差不多的操作:

    譬如可以调用 **read()**或 recv()函数读取网络数据,调用 **write()**或 send()函数发送数据

read()

通过 read()函数从一个文件描述符中读取指定字节大小的数据并放入到指定的缓冲区中;

	read()调用成功将返回读取到的字节数,此返回值受文件剩余字节数限制:
	所以当返回值小于指定的字节数时并不意味着错误;
	这可能是因为当前可读取的字节数小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者 read()函数被信号中断等)。
	出错返回-1 并设置 errno,如果在调 read 之前已到达文件末尾,则这次 read 返回 0。
  • 套接字描述符也是文件描述符,使用 read()读取网络数据,read()函数参数 fd 就是对应的套接字描述符
recv()

  • 不论是客户端还是服务器都可以通过 revc()函数读取网络数据,它与 read()函数的功能是相似的;

  • recv()与 read()相似,但 recv()可以通过指定 flags 标志来控制如何接收数据

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/*参数:
	sockfd:指定套接字描述符;
    buf:   指向了一个数据接收缓冲区;
    len:   指定了读取数据的字节大小,
    flags: 指定一些标志用于控制如何接收数据。
  返回值:
  	调用成功:
  			返回实际读取到的字节数;
  	如果发送者已经调用 shutdown 来结束传输,或者网络协议支持按默认的顺序关闭并且发送端已经关闭:
  			那么当所有的数据接收完毕后,recv 会返回 0。

控制如何接收数据的flags 标志

image-20240817010356336

通常一般我们将 flags 参数设置为 0,当然,你可以根据自己的需求设置该参数。

MSG_PEEK:	
	当指定 MSG_PEEK 标志时,可以查看下一个要读取的数据但不真正取走它,当再次调用 read 或 recv函数时,会返回刚才查看的数据。
MSG_WAITALL:
	对于 SOCK_STREAM 类型套接字,接收的数据可以比指定的字节大小少。MSG_WAITALL 标志会阻止这种行为,知道所请求的数据全部返回,recv 函数才会返回。
	对于 SOCK_DGRAM 和 SOCK_SEQPACKET套接字,MSG_WAITALL 标志并不会改变什么行为,因为这些基于报文的套接字类型一次读取就返回整个报文。
write()

功能:
通过 write()函数可以向套接字描述符中写入数据;

返回值:
成功,返回写入的字节数
失败,返回-1,并设置 errno 变量。

send()

  • send 和 write 很相似向套接字描述符中写入数据

  • 但 send 可以通过参数 flags 指定一些标志,来改变处理传输数据的方式

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/*
send()成功返回,也并不表示连接的另一端的进程就一定接收了数据;
我们所能保证的只是当 send成功返回时,数据已经被无错误的发送到网络驱动程序上。
*/

改变处理传输数据的方式的flags 标志

image-20240817011305192
close()关闭套接字
当不再需要套接字描述符时,可调用 close()函数来关闭套接字,释放相应的资源.

IP 地址格式转换函数


  • 点分十进制字符串和二进制地址转换:

    对于人来说,我们更容易阅读的是点分十进制的 IP 地址,譬如 192.168.1.110、192.168.1.50;
    
    这其实是一种字符串的形式,但是计算机所需要理解的是二进制形式的 IP 地址;
    所以我们就需要在点分十进制字符串和二进制地址之间进行转换。
    
  • 点分十进制字符串和二进制地址之间的转换函数主要有:

    inet_aton、inet_addr、inet_ntoa、inet_ntop、inet_pton 这五个;
    在我们的应用程序中使用它们需要包含头文件<sys/socket.h>、<arpa/inet.h>以及<netinet/in.h>。
    
*inet_aton、inet_addr、inet_ntoa

已经废弃

这些函数可将一个 IP 地址在**点分十进制表示形式和二进制表示形式之间进行转换**;

这些函数**已经废弃**了,基本不用这些函数了,但是在一些旧的代码中可能还会看到这些函数。

完成此类转换工作我们应该使用下面介绍的这些函数

☆inet_ntop、inet_pton

使用这两个函数只需包含<arpa/inet.h>头文件即可!

  • inet_ntop()、inet_pton()与 inet_ntoa()、inet_aton()类似。但它们还支持 IPv6 地址

  • 将二进制 Ipv4 或Ipv6 地址转换成以点分十进制表示的字符串形式,
    或将点分十进制表示的字符串形式转换成二进制 Ipv4 或Ipv6 地址。

inet_pton()函数

作用

​ 将点分十进制表示的字符串形式转换成二进制 Ipv4 或 Ipv6 地址。

原型

int inet_pton(int af, const char *src, void *dst);
/*
参数:
	af:		AF_INET或AF_INET6;
			 AF_INET表示待转换的 Ipv4地址;
			 AF_INET6 表示待转换的是 Ipv6 地址;
	src:
			将字符串 src 转换为二进制地址;
	dst:
			转换后得到的地址存放在参数 dst 所指向的对象中;
		如果参数 af 被指定为 AF_INET,则参数 dst 所指对象应该是一个 struct in_addr 结构体的对象;
		如果参数 af 被指定为 AF_INET6,则参数 dst 所指对象应该是一个 struct in6_addr 结构体的对象。
返回值:
        转换成功:
                返回 1(已成功转换)。
        如果 src 不包含表示指定地址族中有效网络地址的字符串:
                返回 0。
        如果 af 不包含有效的地址族:
                返回-1 并将 errno 设置为 EAFNOSUPPORT。
*/

典例

					/*示例代码 30.3.1 inet_pton()函数使用示例*/
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#define IPV4_ADDR "192.168.1.222"

int main(void)
{
     struct in_addr addr;
    
     inet_pton(AF_INET, IPV4_ADDR, &addr);
     printf("ip addr: 0x%x\n", addr.s_addr);
     exit(0);
}
image-20240817012445914
inet_ntop()函数

作用

  • inet_ntop()执行与 inet_pton()相反操作:将二进制 Ipv4 或 Ipv6 地址转换成点分十进制表示的字符串;

  • ​ 函数inet_ntop()会将 src 指向的二进制 IP 地址转换为点分十进制形式的字符串,并将字符串存放在dts

原型

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
/*
参数:
	af:		AF_INET或AF_INET6;
			 AF_INET表示待转换的 Ipv4地址;
			 AF_INET6 表示待转换的是 Ipv6 地址;
	src:
			参数 src 应指向一个 struct in_addr 结构体对象或 struct in6_addr 结构体对象;
			依据参数 af 而定。
	dst:
			参数 src 指向的二进制 IP 地址转换为点分十进制形式的字符串,
			并将字符串存放在参数 dts所指的缓冲区中,参数 size 指定了该缓冲区的大小。
返回值:
	成功:
			返回 dst 指针。
	如果 size 的值太小:
			将会返回 NULL 并将 errno 设置为ENOSPC;
*/

典例

					/*示例代码 30.3.2 inet_ntop()函数使用示例*/
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(void)
{
     struct in_addr addr;
     char buf[20] = {0};
     
     addr.s_addr = 0xde01a8c0;
     inet_ntop(AF_INET, &addr, buf, sizeof(buf));
     printf("ip addr: %s\n", buf);
     exit(0);
}
image-20240817014130004

socket 编程实战


进行编程实战,实现一个简单地服务器一个简单地客户端应用程序

编写服务器程序

编写服务器应用程序的流程如下:

①、调用 socket()函数打开套接字,得到套接字描述符;

②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定;

③、调用 listen()函数让服务器进程进入监听状态;

④、调用 accept()函数获取客户端的连接请求并建立连接;

⑤、调用 read/recv、write/send 与客户端进行通信;

⑥、调用 close()关闭套接字。

				/*示例代码 30.4.1 简单地服务器应用程序示例代码*/
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
文件名 : socket_server.c
作者 : 邓涛
版本 : V1.0
描述 : 一个简单地 TCP 服务器应用程序示例代码
其他 : 无
论坛 : www.openedv.com
日志 : 初版 V1.0 2021/7/20 邓涛创建
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define SERVER_PORT 8888 //端口号不能发生冲突,不常用的端口号通常大于 5000
int main(void)
{
     struct sockaddr_in server_addr = {0};
     struct sockaddr_in client_addr = {0};
     char ip_str[20] = {0};
     int sockfd, connfd;
     int addrlen = sizeof(client_addr);
     char recvbuf[512];
     int ret;
    
     /* 打开套接字,得到套接字描述符 */
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (0 > sockfd) 
     {
         perror("socket error");
         exit(EXIT_FAILURE);
     }
     /* 将套接字与指定端口号进行绑定 */
     server_addr.sin_family = AF_INET;
     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     server_addr.sin_port = htons(SERVER_PORT);
     ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
     if (0 > ret) 
     {
         perror("bind error");
         close(sockfd);
         exit(EXIT_FAILURE);
     }
    
     /* 使服务器进入监听状态 */
     ret = listen(sockfd, 50);
     if (0 > ret)
     {
         perror("listen error");
         close(sockfd);
         exit(EXIT_FAILURE);
     }
    
     /* 阻塞等待客户端连接 */
     connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
     if (0 > connfd) 
     {
         perror("accept error");
         close(sockfd);
         exit(EXIT_FAILURE);
     }
     printf("有客户端接入...\n");
     inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));
     printf("客户端主机的 IP 地址: %s\n", ip_str);
     printf("客户端进程的端口号: %d\n", client_addr.sin_port);
    
     /* 接收客户端发送过来的数据 */
     for ( ; ; ) 
     {
         // 接收缓冲区清零
         memset(recvbuf, 0x0, sizeof(recvbuf));
         // 读数据
         ret = recv(connfd, recvbuf, sizeof(recvbuf), 0);
         if(0 >= ret) 
         {
             perror("recv error");
             close(connfd);
             break;
         }
         // 将读取到的数据以字符串形式打印出来
         printf("from client: %s\n", recvbuf);
         // 如果读取到"exit"则关闭套接字退出程序
         if (0 == strncmp("exit", recvbuf, 4)) 
         {
             printf("server exit...\n");
             close(connfd);
             break;
         }
     }
    
     /* 关闭套接字 */
     close(sockfd);
     exit(EXIT_SUCCESS);
}

​ 以上我们实现了一个非常简单地服务器应用程序,根据上面列举的步骤完成了这个示例代码,最终的功能是,当客户端连接到服务器之后,客户端会向服务器(也就是本程序)发送数据,在我们服务器应用程序中会读取客户端发送的数据并将其打印出来,就是这么简单的一个功能。

​ SERVER_PORT 宏指定了本服务器绑定的端口号,这里我们将端口号设置为 8888,端口不能与其它服务器的端口号发生冲突,不常用的端口号通常大于 5000。代码就不再解释了,都非常简单!

编写客户端程序

再编写一个简单地客户端应用程序,客户端的功能是连接上小节所实现的服务器,连接成功之后向服务器发送数据,发送的数据由用户输入;

				/*示例代码 30.4.2 简单地客户端应用程序示例代码*/
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
文件名 : socket_client.c
作者 : 邓涛
版本 : V1.0
描述 : 一个简单地 TCP 客户端应用程序示例代码
其他 : 无
论坛 : www.openedv.com
日志 : 初版 V1.0 2021/7/20 邓涛创建
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SERVER_PORT 8888 //服务器的端口号
#define SERVER_IP "192.168.1.150" //服务器的 IP 地址
int main(void)
{
     struct sockaddr_in server_addr = {0};
     char buf[512];
     int sockfd;
     int ret;
    
     /* 打开套接字,得到套接字描述符 */
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (0 > sockfd) 
     {
         perror("socket error");
         exit(EXIT_FAILURE);
     }
    
     /* 调用 connect 连接远端服务器 */
     server_addr.sin_family = AF_INET;
     server_addr.sin_port = htons(SERVER_PORT); //端口号
     inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP 地址
     ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
     if (0 > ret) 
     {
         perror("connect error");
         close(sockfd);
         exit(EXIT_FAILURE);
     }
     printf("服务器连接成功...\n\n");
    
     /* 向服务器发送数据 */
     for ( ; ; ) 
     {
         // 清理缓冲区
         memset(buf, 0x0, sizeof(buf));
         // 接收用户输入的字符串数据
         printf("Please enter a string: ");
         fgets(buf, sizeof(buf), stdin);
         // 将用户输入的数据发送给服务器
         ret = send(sockfd, buf, strlen(buf), 0);
         if(0 > ret)
         {
             perror("send error");
             break;
         }
         //输入了"exit",退出循环
         if(0 == strncmp(buf, "exit", 4))
         break;
     }
    
     close(sockfd);
     exit(EXIT_SUCCESS);
}

​ 代码不再说明!需要注意的是 SERVER_IP 和 SERVER_PORT 指的是服务器的 IP 地址和端口号,服务器的 IP 地址根据实际情况进行设置,服务器应用程序示例代码 30.4.1 中我们绑定的端口号为 8888,所以在客户端应用程序中我们也需要指定 SERVER_PORT 为 8888。

编译测试

我将服务器程序运行在开发板上,而将客户端应用程序运行在 Ubuntu 系统;

当然也可以将客户端和服务器程序都运行在开发板或 Ubuntu 系统,这都是没问题的。

首先编译服务器应用程序和客户端应用程序:

image-20240817014857757

接下来我们便可以在客户端处输入字符串,客户端程序会将我们输入的字符串信息发送给服务器,服务器接收到之后将其打印出来,如下所示:

image-20240817014927502

总结

​ 到此,本章的内容就结束了,内容讲得非常浅,目的其实并不是让大家学会网络编程,这个是不可能的,旨在以引导大家入门为主,让大家对 socket 网络编程有一个基本的了解和认识。

​ 因为网络编程本就是应用编程中一门比较专业的方向,如果大家将来想从事这方面的工作、或者以后从事这方面工作,再去找资料好好学习,如果没有这个打算,那就不要去深入研究这个,有了基本的了解、认识就行了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序引入全局环境变量

有时候一套代码要在多个小程序appId下使用,其中又有一些数据(文字)需要做区分.可以使用下面的方法 把要配置的数据以export default 形式导出 在app.js中,引入project.config.0.js文件,将导出的数据放在globalData中 在页面目录中,即可利用getApp()方法使用全局变量 也可以放数…

LM4863 带立体声耳机功能的双 2.2W音频功率放大器芯片IC

一般概述 LM4863是双桥接的音频功率放大器。当电源电压为5V时&#xff0c;在保证总谐波失真、噪声失真之和小于1.0%的情况下&#xff0c;4Ω负载提供2.2W的输出功率或者可向3Ω负载提供2.5W的输出功率。另外&#xff0c;当驱动立体声耳机时&#xff0c;耳机输入端允许放…

微服务:分布式事务

&#x1f4a5; 该系列属于【SpringBoot基础】专栏&#xff0c;如您需查看其他SpringBoot相关文章&#xff0c;请您点击左边的连接 目录 一、引言 二、Seata 三、部署TC服务 1. 准备数据库表 2. 准备配置文件 3. Docker部署 四、微服务集成Seata 1. 引入依赖 2. 改造配…

json 库的下载与使用

Json 简介Json下载Json::Value 数据对象类Json 序列化/反序列化的介绍Json 的序列化类低版本高版本 Json 的反序列化类低版本高版本 Json序列化操作Json反序列化操作 简介 json 是一种数据交换格式&#xff0c;采用独立于编程语言的文本格式来存储和表示的数据。 Json下载 使…

波导阵列天线单元 学习笔记3 基于空气填充双模馈网的双圆极化膜片天线阵列

摘要&#xff1a; 此通信提出了一种使用空气填充双模馈网的基于膜片极化器的双圆极化天线阵列。一种1分4的圆腔单层覆盖在膜片极化器上来抑制栅瓣。全公司馈网被一个双模传输线所实现&#xff0c;以此在一组馈网内联合了TEM模式&#xff08;由HW悬架线激励&#xff09;和TE10模…

Stable Diffusion赋能“黑神话”——助力悟空走进AI奇幻世界

《黑神话&#xff1a;悟空》是由游戏科学公司制作的以中国神话为背景的动作角色扮演游戏&#xff0c;将于2024年8月20日发售。玩家将扮演一位“天命人”&#xff0c;为了探寻昔日传说的真相&#xff0c;踏上一条充满危险与惊奇的西游之路。 同时&#xff0c;我们还可以借助AI绘…

智能电子班牌源码之终端管理-SAAS本地化及未来之窗行业应用跨平台架构

一智能电子班牌 智能电子班牌为教育行业量身打造&#xff0c;高清显示屏体、可安装各类软件&#xff0c;满足门禁、考勤、信息显示等多种功能。节能防水防误触设计&#xff0c;更适用于校园环境。 二、设备管理 1. 提高效率&#xff1a;管理员无需亲临设备现场&#xff0c;…

Maven-03.idea集成-配置及创建maven项目

一.配置Maven 写在前面&#xff1a;特别注意idea版本与Maven版本以及jdk版本与Maven版本的匹配问题。一定要下载和当前idea版本以及jdk版本匹配的maven版本&#xff0c;否则会出问题。具体匹配结果上网查询&#xff01;此处采用idea2023.2.3&#xff0c;jdk17&#xff0c;mave…

edge浏览器可以,chrome浏览器看不到接口数据

chrome 谷歌浏览器&#xff0c;可以看到页面&#xff0c;F12的开发者工具看不到返回数据 无法加载响应数据: No data found for resource with given identifier Chrome 将显示 ERR_INTERNET_DISCONNECTED 错误 edge浏览器是正常的。 哈哈哈哈哈哈&#xff0c;这里误点了&a…

C ++初阶:类和对象(上)

目录 &#x1f31e;0.前言 1. 面向过程和面向对象初步认识 2..类的引入与定义 2.1类的引入 2.2类的定义 3.类的访问限定符及其封装 3.1访问限定符 3.2封装 4.类的作用域 4.1加餐和发现 5.类的实例化 6.类对象大小的计算 6.1.内部的存储方式 6.2结构体对齐规则回顾…

【闪送-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

【办公软件】安全风险 Microsoft 已阻止宏运行,因为此文件的来源不受信任

Excel 2019版本&#xff0c;就出现安全风险 Microsoft 已阻止宏运行 因为此文件的来源不受信任的问题&#xff0c;宏直接就用不了了。 网上的解决方法&#xff0c;文件右键属性->取消安全锁。但存在没有安全锁这个选项。后查询到一个简单的解决方法。 打开Excel表格->文件…

利用MongoDB进行数据治理,防范构建生成式AI应用程序时的潜在安全风险

生成式人工智能&#xff08;生成式AI&#xff09;正在蓬勃发展&#xff0c;许多企业和初创公司正在运用AI工具来解决各自的用例问题。随着企业逐渐适应市场上的新技术范式转移&#xff0c;开发者社区和开源模型也在不断发展壮大。 构建智能生成式AI应用程序需要灵活运用数据。…

R语言统计分析——线性模型假设的综合验证与多重共线性

参考资料&#xff1a;R语言实战【第2版】 1、线性模型假设的综合验证 gvlma包中的gvlma()函数&#xff0c;能对线性模型进行综合验真&#xff0c;同时还能做偏斜度、峰度和异方差性的评价。也就是说&#xff0c;它给模型提供了一个单独的综合验证&#xff08;通过/不通过&…

RK3568平台开发系列讲解(UART篇)line discipline

🚀返回专栏总目录 文章目录 一、UART 读写过程二、line discipline组成沉淀、分享、成长,让自己和他人都能有所收获!😄 line discipline 介于 TTY 层和具体的串口驱动 ( 比如 serial8250 ) 之间。 一、UART 读写过程 发送数据时: 应用程序通过系统调用向 TTY 设备文件写…

打卡学习Python爬虫第四天|bs4爬取优美图库的小清新图片

bs4解析比较简单&#xff0c;通过HTML的标签和属性去提取值&#xff0c;find(标签,属性"值"&#xff09; 但是需要了解HTML的语法知识&#xff0c;然后再使用bs4去提取&#xff0c;逻辑和编写难度就会比较简单和清晰。 bs4如何使用&#xff1f;如有如下HTML代码&am…

币价与数据持续低迷,比特币和以太坊能否从低谷中恢复?

在过去的一周里&#xff0c;加密货币市场经历了令人失望的表现&#xff0c;比特币和以太坊的价格持续低迷&#xff0c;引发了投资者的广泛关注。尽管宏观经济背景提供了一些利好因素&#xff0c;但市场情绪依然低迷&#xff0c;BTC/USD 和 ETH/USD 均未能打破当前的下行趋势。本…

鸿蒙HarmonyOS开发知识:命令行工具篇—“codelinter”

codelinter同时支持使用命令行执行代码检查与修复&#xff0c;可将codelinter工具集成到门禁或持续集成环境中。 codelinter命令行格式为&#xff1a; codelinter [options] [dir] options&#xff1a;可选配置&#xff0c;请参考表1。 dir&#xff1a;待检查的工程根目录…

在项目中运用os 模块获取本机ip地址并运用到终端启动成功打开network

一、安装os模块 os 模块是 Node.js 的内置模块&#xff0c;不需要额外安装 二、在项目中新建一个名为 getLocalIpAddress.js 的文件 // getLocalIpAddress.js const os require(os);function getLocalIpAddress() {const interfaces os.networkInterfaces();for (const na…

LNMP 架构(Linux+NGINX+memcache+PHP)

目录 1 源码编译PHP与NGINX 1.1 NGINX 源码编译 1.2 PHP 源码编译安装 2 实现PHP与NGINX的连接 2.1 php-fpm的详细介绍 2.2 LNMP与LAMP的区别 2.3 PHP配置文件的介绍 2.4 实例实现php-fpm 与 NGINX的连接 2.4.1 指定pid的存放位置 2.4.2 php-fpm设置监听自己端口与IP 2.4.3 主配…