“掌握Linux网络编程艺术,构建跨平台的网络通信解决方案!“#Linux系统编程之网络编程

本文主要是介绍“掌握Linux网络编程艺术,构建跨平台的网络通信解决方案!“#Linux系统编程之网络编程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

"掌握Linux网络编程艺术,构建跨平台的网络通信解决方案!"#Linux系统编程之网络

    • 前言
    • 预备知识
    • 一、 网络编程概述
      • 1.1 网络编程概述图
      • 1.2 TCP/UDP对比(面试)
      • 1.3 端口号的作用
    • 二、 字节序
      • 2.1 大端字节序(Big-Endian)
      • 2.2 小端字节序(Little-Endian)
      • 2.3 字节序的重要性
      • 2.4 字节序转换函数
      • 2.5 字节序示意图
    • 三、 socket编程步骤
    • 四、 Linux提供的网络编程API简析
      • 4.1 socket函数介绍
        • 4.1.1 socket函数原型
        • 4.1.2协议族 (domain)
        • 4.1.3 套接字类型 (type)
        • 4.1.4 协议 (protocol)
      • 4.2 bind函数介绍
        • 4.2.1 bind函数原型
        • 4.2.2 bind函数参数
        • 4.2.3 addr结构体详解
      • 4.3 地址转换API
        • 4.3.1 inet_aton函数介绍
        • 4.3.2 inet_ntoa函数
      • 4.4 listen函数介绍
        • 4.4.1 listen函数原型
        • 4.4.2 listen函数功能
        • 4.4.3 内核维护的队列
        • 4.4.4 listen函数参数
      • 4.5 accept函数介绍
        • 4.5.1 accept函数原型
        • 4.5.1 accept函数功能
        • 4.5.2 accept函数参数
        • 4.5.3 accept函数返回值
      • 4.6 数据收发函数(一)
        • 4.6.1 write()函数
        • 4.6.2 read()函数
        • 4.6.3 网络I/O 相关函数
        • 4.6.4 recvfrom() 和 sendto()
      • 4.7 数据收发函数(二)
        • 4.7.1 TCP套接字数据发送与接收函数
      • 4.8 connect函数介绍
        • 4.8.1 connect函数原型
        • 4.8.2 connect函数功能
        • 4.8.3 connect函数参数
        • 4.8.4 connect函数返回值
    • 五 、scoket服务端代码实现一
      • 5.1 字节序转换函数详细介绍
        • 5.1.1htons() 和 htonl()
        • 5.1.2 ntohs() 和 ntohl()
      • 5.2 scoket服务端实现客户端连接程序
        • 5.2.1 程序代码
        • 5.2.2 程序运行结果
    • 六、 socket服务端代码实现二
      • 6.1 socket服务端实现客户端连接收发数据程序
        • 6.1.1 程序代码
        • 6.1.2 运行结果
    • 七、 socket客户端代码实现
      • 7.1 socket客户端连接服务器发送接收数据程序
        • 7.1.1 程序代码
        • 7.1.2 程序运行结果
    • 八、 socket 客户端与服务器双方聊天
      • 8.1 socket服务器实现多个客户端连接
        • 8.1.1 客户端程序代码
        • 8.1.2 服务器程序代码
        • 8.1.3 客户端服务器配合运行结果
      • 8.2 socket 服务器和客户端实现双方聊天
        • 8.2.1 客户端程序代码
        • 8.2.2 服务器程序代码
        • 8.2.3 客户端服务器配合运行结果
    • 九、 socket 服务器连接多客户端通信
      • 9.1 资源竞争简图
      • 9.2 服务器自动回复解决资源竞争程序代码
      • 9.3 客户端程序代码和1.2.1一致
      • 9.4 多个客户端配合服务器运行结果
    • 结束语

前言

  本篇博文深入探讨了Linux系统下的网络编程,内容涵盖网络编程的概览、字节序的重要性、socket编程的基本步骤,以及Linux系统提供的丰富网络编程API的简要分析。接下来,我们将逐步展示两个不同实现的socket服务端代码示例,一个socket客户端的代码实现,进而展示如何通过socket实现客户端与服务器之间的双向聊天功能,并最终探讨如何设计一个socket服务器以同时连接并处理多个客户端的通信。
  对于看到这篇博文的朋友,如果您觉得内容有价值,不妨先点个赞,再继续深入阅读,相信您会收获满满!

预备知识

  一、C变量
  二、基本输入输出
  三、流程控制
  四、函数
  五、指针
  六、字符串
  七、结构体
  八、联合体
  九、Linux系统基本操作命令如mkdir,ls -l等。
  十、Linux系统编程之进程的知识

  如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!

一、 网络编程概述

1.1 网络编程概述图

请添加图片描述

1.2 TCP/UDP对比(面试)

  1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前 不需 要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
  3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
  4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  5. CP首部开销20字节;UDP的首部开销小,只有8个字节
  6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

1.3 端口号的作用

  台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等
  这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。
  实际上是通过“IP地址+端口号”来区 分不同的服务的。
  端口提供了一种访问通道,
  服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69
  在Linux网络编程中通常用5000~10000的端口号

二、 字节序

  在Linux系统下,网络编程中的字节序是一个重要的概念,它涉及到多字节数据在计算机内存中存储或在网络中传输时的字节排列顺序。字节序主要分为两种:大端字节序(Big-Endian)小端字节序(Little-Endian)。

2.1 大端字节序(Big-Endian)

  • 定义:·高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。·这种字节序也被称为网络字节序,因为TCP/IP协议栈采用大端字节序作为网络传输的标准。
  • 示例:对于32位整数0x12345678,在大端字节序下,其在内存中的存储顺序为0x12 0x34 0x56 0x78。

2.2 小端字节序(Little-Endian)

  • 定义低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。这种字节序常见于某些CPU架构,如x86系列。
  • 示例:对于同样的32位整数0x12345678,在小端字节序下,其在内存中的存储顺序为0x78 0x56 0x34 0x12。

2.3 字节序的重要性

  在网络编程中,由于不同的计算机可能采用不同的字节序,因此在数据交换时需要进行字节序的转换,以确保数据的正确解析。例如,当一台采用小端字节序的计算机向采用大端字节序的计算机发送数据时,发送方需要将数据从主机字节序(小端)转换为网络字节序(大端),接收方则需要进行相反的转换。

2.4 字节序转换函数

  Linux系统提供了一系列函数用于字节序的转换,这些函数定义在<arpa/inet.h>头文件中:

  • htonl()htons():分别用于将无符号长整型(32位)和无符号短整型(16位)从主机字节序转换为网络字节序。
  • ntohl()ntohs():分别用于将无符号长整型无符号短整型从网络字节序转换回主机字节序。

2.5 字节序示意图

请添加图片描述

三、 socket编程步骤

请添加图片描述

四、 Linux提供的网络编程API简析

4.1 socket函数介绍

4.1.1 socket函数原型
int socket(int domain, int type, int protocol);
4.1.2协议族 (domain)
  • 指明所使用的协议族,通常为 AF_INET,表示互联网协议族(TCP/IP协议族):
    • AF_INET:IPv4因特网域
    • AF_INET6:IPv6因特网域
    • AF_UNIX:Unix域
    • AF_ROUTE:路由套接字
    • AF_KEY:密钥套接字
    • AF_UNSPEC:未指定
4.1.3 套接字类型 (type)
  • 指定socket的类型:
    • SOCK_STREAM
      流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。
    • SOCK_DGRAM
      数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP
    • SOCK_RAW
      允许程序使用低层协议,原始套接字允许对底层协议如IPICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
4.1.4 协议 (protocol)
  • 通常赋值 “0”
  • 0 选择 type 类型对应的默认协议:
  • IPPROTO_TCP:TCP传输协议
  • IPPROTO_UDP:UDP传输协议
  • IPPROTO_SCTP:SCTP传输协议
  • IPPROTO_TIPC:TIPC传输协议

4.2 bind函数介绍

  bind()函数用于将特定的IP地址和端口号与给定的套接字(socket)描述符关联起来。这是网络通信中,特别是在使用TCP/IP协议进行服务器编程时的一个重要步骤。

4.2.1 bind函数原型
#include <sys/types.h>
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
4.2.2 bind函数参数
  • sockfd:是一个socket描述符,它是通过之前调用socket()函数获得的。
  • addr:是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针。这个指针指向要绑定给sockfd的协议地址结构。这个地址结构根据创建socket时的地址协议族(如IPv4或IPv6)的不同而不同。
  • addrlen:是addr参数所指向的地址结构的长度,这个长度可以通过sizeof运算符获得。
4.2.3 addr结构体详解
// sockaddr 结构体是一个通用的套接字地址结构体,用于存储各种协议的地址信息
// IPv4对应的是sockaddr_in结构体,它是sockaddr的一个特例// sockaddr结构体的定义(简化版,非标准实现,仅用于说明)
struct sockaddr {unsigned short sa_family; // 协议族,如AF_INET表示IPv4char sa_data[14];         // IP地址和端口号等数据,实际使用中会被sockaddr_in等结构体替代// 注意:这里的sa_data数组大小仅为示例,实际定义中并不存在
};// sockaddr_in结构体,用于表示IPv4地址
struct sockaddr_in {sa_family_t sin_family;   // 协议族,AF_INET表示IPv4in_port_t sin_port;       // 端口号,网络字节序struct in_addr sin_addr;  // IP地址结构体unsigned char sin_zero[8];// 填充,无实际意义,仅用于与sockaddr结构体在内存中对齐// 这样sockaddr_in和sockaddr结构体之间才能相互转换
};
//struct in_addr 结构体内容
struct in_addr {__be32  s_addr;  网络IP地址
};// 示例:如何设置sockaddr_in结构体
// 注意:在实际编程中,不会直接对sockaddr_in的sin_addr成员进行赋值,而是会通过
// inet_pton()等函数将点分十进制的IP地址字符串转换为in_addr结构体
// 下面的代码仅为展示结构体成员,不代表实际可运行的赋值操作
struct sockaddr_in addr;
addr.sin_family = AF_INET;       // 设置协议族为IPv4
addr.sin_port = htons(12345);    // 设置端口号为12345,htons()用于主机字节序到网络字节序的转换
// 假设我们有一个点分十进制的IP地址字符串"192.168.1.1",则需要转换后赋值给sin_addr
// 但这里仅展示如何声明和初始化sockaddr_in结构体,不涉及具体的转换操作

4.3 地址转换API

4.3.1 inet_aton函数介绍

  inet_aton 函数是一个用于将点分十进制的IPv4地址字符串(如"192.168.1.123")转换为网络字节序的二进制IP地址形式的函数。这个函数在处理IPv4地址时非常有用,特别是在需要将IP地址作为套接字编程的一部分时。

  函数原型

#include <arpa/inet.h>int inet_aton(const char *straddr, struct in_addr *addrp);
  • 参数

    • const char *straddr:指向点分十进制IP地址字符串的指针。
    • struct in_addr *addrp:指向in_addr结构体的指针,该结构体用于存储转换后的网络字节序的IP地址。
  • 返回值

    • 如果转换成功,返回非零值(通常为1)。
    • 如果转换失败(例如,由于无效的IP地址格式),返回0。
4.3.2 inet_ntoa函数

  inet_ntoa 函数则是inet_aton的逆操作,它将网络字节序的二进制IP地址(存储在in_addr结构体中)转换回点分十进制的字符串形式。

  函数原型

#include <arpa/inet.h>char *inet_ntoa(struct in_addr inaddr);
  • 参数

    • struct in_addr inaddr:包含网络字节序的二进制IP地址的in_addr结构体。
  • 返回值

    • 返回一个指向静态分配内存的字符串的指针,该字符串包含了点分十进制的IP地址。注意,这个字符串是静态分配的,意味着它在inet_ntoa的后续调用中可能会被覆盖。

4.4 listen函数介绍

4.4.1 listen函数原型
#include<sys/types.h>
#include<sys/socket.h>
int listen(int sockfd, int backlog);
4.4.2 listen函数功能
  • 设置能处理的最大连接数listen()函数并未开始接受连接,只是将套接字设置为监听模式。该函数仅用于服务器端。服务器进程不知道要与哪个客户端连接,因此它不会主动要求与某个进程连接,而是一直监听是否有其他客户进程与之连接,并响应该连接请求。一个服务进程可以同时处理多个客户进程的连接。
  • 主要功能有两个:
    1. 将一个未连接的套接字转换为一个被动套接字(监听状态)。
    2. 规定内核为相应套接字排队的最大连接数。
4.4.3 内核维护的队列
  • 未完成连接队列
    • 每个SYN报文段对应其中一项。这些报文段由客户端发出并到达服务器,而服务器正在等待完成相应的TCP三次握手过程。这些套接字处于SYN_RCVD状态。
  • 已完成连接队列
    • 每个已完成TCP三次握手过程的客户端对应其中一项。这些套接字处于ESTABLISHED状态。
4.4.4 listen函数参数
  • sockfd:socket系统调用返回的服务器端socket描述符。
  • backlog:指定在请求队列中允许的最大请求数。

4.5 accept函数介绍

4.5.1 accept函数原型
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
4.5.1 accept函数功能

  accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。

4.5.2 accept函数参数
  • sockfd:socket系统调用返回的服务器端socket描述符。
  • addr:用来返回已连接的对端(客户端)的协议地址。
  • addrlen:客户端地址长度。
4.5.3 accept函数返回值
  • 该函数的返回值是一个新的套接字描述符,这个描述符表示已连接的套接字,而第一个参数sockfd是服务器监听套接字描述符。一个服务器通常仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示TCP三次握手已完成)。当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。

4.6 数据收发函数(一)

  在套接字通信中,进行字节读取的函数主要有read()write(),它们与标准I/O中的读取函数略有区别。这是因为在使用套接字时,实际输入或输出的字节数可能比请求的要少。

4.6.1 write()函数
  • 原型ssize_t write(int fd, const void *buf, size_t nbytes);
  • 说明:此函数将buf中的nbytes个字节写入到文件描述符fd中。成功时返回写入的字节数,出错则返回-1。
4.6.2 read()函数
  • 原型ssize_t read(int fd, void *buf, size_t nbyte);
  • 说明:此函数从文件描述符fd中读取nbyte个字节到buf中,返回实际读取的字节数。如果出错,则返回-1。
4.6.3 网络I/O 相关函数

  除了read()write()外,网络I/O还提供了其他函数,如:

  • recv() / send()
  • readv() / writev()
  • recvmsg() / sendmsg()
  • recvfrom() / sendto()

这些函数提供了更灵活和强大的网络通信能力。

4.6.4 recvfrom() 和 sendto()
  • recvfrom():用于从指定的套接字接收数据,并可以获取数据发送者的地址。
  • sendto():用于向指定的地址发送数据。

  这些函数特别适用于UDP等无连接的协议,因为它们允许直接指定数据的目标地址。

4.7 数据收发函数(二)

4.7.1 TCP套接字数据发送与接收函数

  发送数据函数

  在TCP套接字上发送数据的函数是 send()。该函数的使用包含以下三个关键要素:

  • 套接字s:表示已经建立连接的套接字描述符,即 accept() 函数的返回值。
  • 待发数据msg:指向存放待发送数据的缓冲区。
  • 数据长度len:待发送数据的长度。

函数原型:

ssize_t send(int s, const void *msg, size_t len, int flags);
  • 参数flags:控制选项,一般设置为0。

  接收数据函数

  在TCP套接字上接收数据的函数是 recv()。该函数同样包含三个关键要素:

  • 套接字s:面向连接的套接字描述符。
  • 接收缓冲区buf:用于存储接收到的数据。
  • 长度len:接收缓冲区的长度。

函数原型:

ssize_t recv(int s, void *buf, size_t len, int flags);
  • 参数flags:控制选项,一般设置为0。

4.8 connect函数介绍

  connect函数:客户机连接主机

4.8.1 connect函数原型
- `#include <sys/types.h>`
- `#include <sys/socket.h>`
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
4.8.2 connect函数功能
  • 该函数用于绑定之后的client端(客户端),与服务器建立连接。
4.8.3 connect函数参数
  • sockfd:是目的服务器的socket描述符。
  • addr:是服务器端的IP地址和端口号的地址结构指针。
  • addrlen:地址长度,常被设置为sizeof(struct sockaddr)
4.8.4 connect函数返回值
  • 成功时返回0,遇到错误时返回-1,并且errno中包含相应的错误码。

五 、scoket服务端代码实现一

5.1 字节序转换函数详细介绍

5.1.1htons() 和 htonl()
  • 功能:将主机字节序的值转换为网络字节序(大端字节序)。
  • 参数
    • uint16_t host16bitvaluehtons()):表示16位的主机字节序值,需要被转换为网络字节序。
    • uint32_t host32bitvaluehtonl()):表示32位的主机字节序值,同样需要被转换为网络字节序。
  • 返回值
    • htons() 返回一个uint16_t类型的值,即转换后的16位网络字节序值。
    • htonl() 返回一个uint32_t类型的值,即转换后的32位网络字节序值。
5.1.2 ntohs() 和 ntohl()
  • 功能:将网络字节序的值转换回主机字节序。
  • 参数
    • uint16_t net16bitvaluentohs()):表示从网络上接收到的16位网络字节序值,需要被转换回主机字节序。
    • uint32_t net32bitvaluentohl()):表示从网络上接收到的32位网络字节序值,同样需要被转换回主机字节序。
  • 返回值
    • ntohs() 返回一个uint16_t类型的值,即转换后的16位主机字节序值。
    • ntohl() 返回一个uint32_t类型的值,即转换后的32位主机字节序值。

这两个函数通常在网络通信的接收端使用,以确保接收到的数据能够被本地计算机正确理解和处理。

5.2 scoket服务端实现客户端连接程序

5.2.1 程序代码
#include <stdio.h>
使用socket函数需要包含以下头文件
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>#include <stdlib.h>int main()
{int s_fd = 0;				定义socket网络描述符struct sockaddr_in s_addr;	定义网络信息结构体变量int a_fd = 0;               定义accept连接描述符//1 sockets_fd = socket(AF_INET,SOCK_STREAM,0); 建立IPV4,TCP协议的服务器if(s_fd == -1)				判断是否建立成功{perror("socket");exit(-1);}//2.bind//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);s_addr.sin_family = AF_INET;		配置IPV4s_addr.sin_port   = htons(8989);	配置端口号inet_aton("192.168.208.132",&s_addr.sin_addr);	配置IP地址bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));	将服务器绑定信息//3.listen//int listen(int sockfd, int backlog);listen(s_fd,10);					将服务器设置为监听状态,监听10//4.accept//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);a_fd = accept(s_fd,NULL,NULL);	    将服务器设置为待连接状态,一但客户端连接就返回连接描述符//5.read//6.writeprintf("successful con\n");		连接成功后输出提示信息while(1);						防止程序退出return 0;
}
5.2.2 程序运行结果

  如下两图
在这里插入图片描述
在这里插入图片描述

六、 socket服务端代码实现二

6.1 socket服务端实现客户端连接收发数据程序

6.1.1 程序代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main()
{int s_fd = 0;int a_fd = 0;int n_read = 0;struct sockaddr_in s_addr;struct sockaddr_in c_addr;		  定义客户端网络信息结构体变量char readBuf[128];memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));	对客户端网络信息结构体变量清空//1 sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}//2.bind//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);s_addr.sin_family = AF_INET;s_addr.sin_port   = htons(8989);inet_aton("192.168.208.132",&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen//int listen(int sockfd, int backlog);listen(s_fd,10);//4.accept//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int c_len = sizeof(struct sockaddr_in);			计算客户端网络信息结构体变量大小 a_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len); 若连接客户端返回客户端IP地址和网络连接描述符if(a_fd == -1)	判断是否连接成功{perror("connet");      失败输出失败信息}else{printf("connet is : %s\n",inet_ntoa(c_addr.sin_addr));  成功输出客户端地址}//5.readn_read = read(a_fd,readBuf,128);		读取客户端发送的数据if(n_read == -1)			判断是否读取成功{perror("read");			失败输出失败原因}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);  成功输出读取到的字节数和数据}//6.writewrite(a_fd,"I get your data",strlen("I get your data"));	向客户端发送信息while(1);					防止程序退出,以便用Windows CMD连接,不然会连接不上,或者连接上会出现发送数据后立即断开连接。return 0;
}
6.1.2 运行结果

  如下两图
在这里插入图片描述
在这里插入图片描述

七、 socket客户端代码实现

7.1 socket客户端连接服务器发送接收数据程序

7.1.1 程序代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main()
{int c_fd = 0;int n_read = 0;struct sockaddr_in c_addr;char readBuf[128];memset(&c_addr,0,sizeof(struct sockaddr_in));//1 socketc_fd = socket(AF_INET,SOCK_STREAM,0);        获取socket网络描述符if(c_fd == -1){perror("socket");exit(-1);}//2.connect									连接服务器c_addr.sin_family = AF_INET;				配置IPV4c_addr.sin_port   = htons(8989);			配置端口号inet_aton("192.168.208.132",&c_addr.sin_addr);	配置IP地址//int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1)							连接服务器并判断是否成功连接服务器{perror("connect");exit(-1);}//3.write								    向服务器发送数据write(c_fd,"I send the data",strlen("I send the data"));//4.read									接收服务器的回信n_read = read(c_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);}while(1);									防止程序退出return 0;
}
7.1.2 程序运行结果

  如下图
在这里插入图片描述

八、 socket 客户端与服务器双方聊天

8.1 socket服务器实现多个客户端连接

8.1.1 客户端程序代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main(int argc,char **argv)
{int c_fd = 0;int n_read = 0;struct sockaddr_in c_addr;char readBuf[128];memset(&c_addr,0,sizeof(struct sockaddr_in));//1 socketc_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}//2.connectc_addr.sin_family = AF_INET;c_addr.sin_port   = htons(atoi(argv[2]));    将端口号配置为默认参数三inet_aton(argv[1],&c_addr.sin_addr);		 将IP地址配置为默认参数二//int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){perror("connect");exit(-1);}//3.writewrite(c_fd,"I send the data",strlen("I send the data"));//4.readn_read = read(c_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);}return 0;
}
8.1.2 服务器程序代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main(int argc,char **argv)
{int s_fd = 0;int a_fd = 0;int n_read = 0;int c_len = 0;struct sockaddr_in s_addr;struct sockaddr_in c_addr;char readBuf[128];memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1 sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}//2.bind//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);s_addr.sin_family = AF_INET;s_addr.sin_port   = htons(atoi(argv[2]));      将端口号配置为默认参数三inet_aton(argv[1],&s_addr.sin_addr);		   将IP地址配置为默认参数二bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen//int listen(int sockfd, int backlog);listen(s_fd,10);//4.accept//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);while(1)		使用while(1)死循环,配置服务器一直允许连接状态{	c_len = sizeof(struct sockaddr_in); a_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len); 配置服务器允许连接if(a_fd == -1){perror("connet");}else{printf("connet is : %s\n",inet_ntoa(c_addr.sin_addr));}if(fork()==0)			当有客户端连接服务器后,会新建一个子进程用于读取客户端传回的信息和回客户端信{//5.readn_read = read(a_fd,readBuf,128);	读取客户端发送的信息if(n_read == -1){perror("read");}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);	输出客户端发送的信息}//6.writewrite(a_fd,"I get your data",strlen("I get your data"));   给客户端回信}}return 0;
}
8.1.3 客户端服务器配合运行结果

  如下图
在这里插入图片描述

8.2 socket 服务器和客户端实现双方聊天

8.2.1 客户端程序代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>int main(int argc,char **argv)
{int c_fd = 0;int n_read = 0;struct sockaddr_in c_addr;char readBuf[128];char sendBuf[128];memset(&c_addr,0,sizeof(struct sockaddr_in));//1 socketc_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}//2.connectc_addr.sin_family = AF_INET;c_addr.sin_port   = htons(atoi(argv[2]));inet_aton(argv[1],&c_addr.sin_addr);//int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){perror("connect");exit(-1);}while(1){if(fork()==0)		新建一个进程用于向服务器发送数据{while(1){//3.writememset(sendBuf,'\0',sizeof(sendBuf));     每次发送数据前对发送数据缓冲区进行清零printf("input: ");gets(sendBuf);							  从键盘获取发送的数据write(c_fd,sendBuf,strlen(sendBuf));	  向服务器发送数据}}while(1){//4.readmemset(readBuf,'\0',sizeof(readBuf));		  每次接收服务器数据前对接收数据缓冲区进行清零n_read = read(c_fd,readBuf,128);			  接收服务器数据if(n_read == -1){perror("read");}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);  输出接收到的服务器数据}}	}return 0;
}
8.2.2 服务器程序代码
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main(int argc,char **argv)
{int s_fd = 0;int a_fd = 0;int n_read = 0;int c_len = 0;struct sockaddr_in s_addr;struct sockaddr_in c_addr;char readBuf[128];char sendBuf[128];memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1 sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}//2.bind//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);s_addr.sin_family = AF_INET;s_addr.sin_port   = htons(atoi(argv[2]));inet_aton(argv[1],&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen//int listen(int sockfd, int backlog);listen(s_fd,10);//4.accept//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);while(1){	c_len = sizeof(struct sockaddr_in); a_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);     使用while(1)死循环,配置服务器一直允许连接状态if(a_fd == -1){perror("connet");}else{printf("connet is : %s\n",inet_ntoa(c_addr.sin_addr));}if(fork()==0)			当有客户端连接服务器后,会新建一个子进程用于接收客户端发送的数据{//6.writeif(fork() == 0)     再新建一个进程用于回客户端的消息{while(1){memset(sendBuf,'\0',sizeof(sendBuf));   每次回复前清楚发送缓冲区的数据printf("intput: ");gets(sendBuf);						    从键盘获取回复数据write(a_fd,sendBuf,strlen(sendBuf));	向客户端发送数据}}while(1){//5.readmemset(readBuf,'\0',sizeof(readBuf));		每次接收数客户端数据前对接收数据缓冲区进行清除n_read = read(a_fd,readBuf,128);			接收客户端发送的信息if(n_read == -1){perror("read");}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);						输出接收到的数据}}}}return 0;
}
8.2.3 客户端服务器配合运行结果

  如下图
在这里插入图片描述

九、 socket 服务器连接多客户端通信

9.1 资源竞争简图

请添加图片描述

9.2 服务器自动回复解决资源竞争程序代码

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main(int argc,char **argv)
{int s_fd = 0;int a_fd = 0;int n_read = 0;int c_len = 0;int mark = 0;struct sockaddr_in s_addr;struct sockaddr_in c_addr;char readBuf[128];char sendBuf[128];memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1 sockets_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}//2.bind//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);s_addr.sin_family = AF_INET;s_addr.sin_port   = htons(atoi(argv[2]));inet_aton(argv[1],&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen//int listen(int sockfd, int backlog);listen(s_fd,10);//4.accept//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);while(1){	c_len = sizeof(struct sockaddr_in); a_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);if(a_fd == -1){perror("connet");}else{printf("connet is : %s\n",inet_ntoa(c_addr.sin_addr));}mark++;            每一个新的客户端连接就会加一if(fork()==0){//6.writeif(fork() == 0){while(1){memset(sendBuf,'\0',sizeof(sendBuf));sprintf(sendBuf,"Successful receive Client %d data",mark);  自动回复的内容write(a_fd,sendBuf,strlen(sendBuf));  进行自动回复sleep(3);}}while(1){//5.readmemset(readBuf,'\0',sizeof(readBuf));n_read = read(a_fd,readBuf,128);if(n_read == -1){perror("read");}else{printf("n_read = %d, get data = %s\n",n_read,readBuf);}}}}return 0;
}

9.3 客户端程序代码和1.2.1一致

9.4 多个客户端配合服务器运行结果

  如下图
在这里插入图片描述

结束语

  非常感谢您的耐心阅读!在您即将离开之际,如果您觉得内容有所收获或启发,不妨动动手指,点个赞再走,这将是对我莫大的鼓励和支持。衷心感谢您的点赞与关注,期待未来能继续为您带来更多有价值的内容!谢谢您!

这篇关于“掌握Linux网络编程艺术,构建跨平台的网络通信解决方案!“#Linux系统编程之网络编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145944

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了