【C++】网络编程之Socket初体验-超详细!

1:Socket(套接字)之初步了解

套接字(Socket)是网络编程中一个非常基本的概念,它是网络通信过程中端点的抽象表示,允许程序之间的数据交换。在计算机网络中,套接字用于实现不同主机上的进程通信。

1.1套接字的概念

套接字是一种通信机制,它为进程间的通信提供了一个端点。在网络中,每个进程都可以通过一个唯一的地址来识别,这个地址由IP地址和端口号组成。因此,一个套接字通常由以下三部分组成:
协议族(通常为AF_INET,表示IPv4)
端口号(一个16位的数字,用于区分同一主机上运行的不同服务)
IP地址(用于标识主机)

1.2套接字的类型

套接字主要有两种类型:
流套接字(SOCK_STREAM):提供顺序、可靠、双向连接的字节流。流套接字使用传输控制协议(TCP)作为传输层协议,确保数据的可靠传输。
数据报套接字(SOCK_DGRAM):提供无连接的服务,数据以数据报的形式发送。每个数据报都是独立的,不保证顺序和可靠性。数据报套接字使用用户数据报协议(UDP)作为传输层协议。

1.3套接字的声明周期

一个套接字的生命周期通常包括以下几个步骤:
创建:使用socket()函数创建一个新的套接字。
绑定:使用bind()函数将套接字与一个地址和端口绑定,这样它就可以开始监听或接收数据。
监听(仅对服务器):服务器使用listen()函数监听来自客户端的连接请求。
接受(仅对服务器):服务器使用accept()函数接受客户端的连接请求,创建一个新的套接字用于与客户端通信。
连接(仅对客户端):客户端使用connect()函数发起对服务器的连接请求。
数据传输:使用send()和recv()(或read()和write())函数在客户端和服务器之间传输数据。
关闭:使用close()函数关闭套接字,释放资源。

1.4套接字的编程流程

1.4.1服务器端:

创建套接字。
绑定套接字到一个地址和端口。
监听端口上的连接请求。
接受客户端的连接请求。
使用接受的套接字进行数据传输。
关闭套接字。

1.4.2客户端:

创建套接字。
连接到服务器的地址和端口。
使用连接的套接字进行数据传输。
关闭套接字。

1.5套接字编程的API

在C和C++中,套接字编程通常使用以下系统调用:
socket():创建一个新的套接字。
bind():将套接字绑定到特定的IP地址和端口。
listen():使套接字进入监听状态,等待连接请求。
accept():接受一个连接请求,创建一个新的套接字用于通信。
connect():发起一个连接请求到服务器。
send() 和 recv():发送和接收数据。
close():关闭套接字。

1.6套接字编程的注意事项

错误处理:网络编程中,错误处理非常重要。每个系统调用都可能失败,需要检查返回值并适当处理错误。
异步处理:网络操作可能是异步的,需要考虑如何处理阻塞和非阻塞操作。
安全性:网络通信可能面临安全威胁,需要考虑加密和验证机制。

2.套接字编程之初体验

准备:这里我只用了一台Ubuntu虚拟机,Xshell(开两个窗口,一个服务器,一个客户端),VSCode(比较喜欢用VSCode连虚拟机写代码,直接Vim也行)。OK,开工!

2.1:服务端(server.cpp)

这里所有的解释就都写在注释了,应该算是写得详细了!

#include <iostream> 
#include <cstring>
#include <sys/socket.h>  //包含socket函数和数据结构
#include <netinet/in.h>  //包含Internet地址簇
#include <unistd.h>     //包含标准符号常数和类型

using namespace std;
const int PORT=1202;

int main(){
    int server_fd,new_socket;//定义套接字文件描述符
    /*server_fd表示服务器的文件描述符,在网络编程中,套接字(socket)通过文件描述符来操作,就像操作文件一样。
    new_socket表示新的连接套接字,当服务器接受客户端的连接请求时,它会创建一个新的套接字来处理这个连接,
    这个新的套接字通过 new_socket 变量来表示。*/
    struct sockaddr_in address;//定义地址结构体
    /*sockaddr_in 结构体是一个非常重要的数据结构,它用于存储网络地址信息。
    这个结构体是 sockaddr 结构体的一个特化版本,专门用于IPv4地址。在 <netinet/in.h> 头文件中定义*/
    int addrlen=sizeof(address);//地址长度
    char buffer[1024]={0};//定义缓冲区
    /*定义了一个大小为1024字节的字符数组,用于存储接收到的数据或将要发送的数据。{0} 初始化数组中的所有元素为0,
    这是一个常见的做法,用于确保缓冲区不包含任何随机数据。当然了,它还有:保证数据完整性,提高效率,数据格式化,
    防止数据丢失等功能,这里是简单示例,主要就是用于存储接收到的数据或将要发送的数据,就不做解释了,*/
    const char *message="在想丽宝啊!";//定义服务器发送的消息,使用 const 可以防止函数意外修改字符串内容。安全

    //创建套接字,用于在C++网络编程中创建一个TCP套接字的。
    if((server_fd=socket(AF_INET,SOCK_STREAM,0))==0){
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    /*socket() 是一个系统调用,用于创建一个新的套接字。它返回一个文件描述符,
    该文件描述符是用于后续套接字操作(如绑定、监听、连接等)的索引。
    AF_INET表示IPv4地址族,用于创建一个基于IPv4的套接字。
    SOCK_STREAM表示创建一个提供序列化、可靠、双向连接的字节流套接字,这通常用于TCP连接。
    传递0表示使用默认的协议,对于AF_INET和SOCK_STREAM,这意味着使用TCP协议。
    == 0检查server_fd是否为0,即检查套接字是否创建失败。
    perror()函数将打印出错误信息到标准错误流
    exit()函数用于终止当前程序,并返回一个状态码给操作系统。EXIT_FAILURE是一个宏,通常定义为非零值,表示程序异常终止
*/

    //设置套接字选项,允许地址重用
    int opt=1;
    if(setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt))){
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    /*opt这个变量用于表示套接字选项的状态,1 表示启用该选项。
    setsockopt() 是一个系统调用,用于设置套接字选项。它可以改变套接字的行为,例如如何接收数据或者如何处理特殊的网络条件。
    SOL_SOCKET是一个指定套接字选项级别的常量,表示这是针对套接字本身的选项,而不是针对某个特定协议的。
    SO_REUSEADDR 是一个套接字选项,允许套接字绑定到一个已经被使用(在TIME_WAIT状态)的本地地址和端口。
    SO_REUSEPORT 是一个扩展选项,允许多个套接字绑定到同一个端口,只要它们的地址不同。
    如果 setsockopt() 返回非零值,表示设置选项失败。
    */

    //绑定套接字到端口
    address.sin_family=AF_INET;//地址簇,
    address.sin_addr.s_addr=INADDR_ANY;//地址。INADDR_ANY表示接受任意IP的连接
    address.sin_port=htons(PORT);//端口号,htons函数将主机字节序转换为网络字节序
    /*这三段代码是设置服务器端套接字地址信息的一部分,它们配置了服务器将监听的协议族、IP地址和端口号
    sin_family 是 sockaddr_in 结构体的一个成员,它指定了地址族。设置 sin_family 为 AF_INET 表示服务器将使用 IPv4 协议。
    sin_addr 是 sockaddr_in 结构体的一个成员,它是一个 in_addr 结构体,用于存储 IPv4 地址。
    INADDR_ANY 是一个特殊的常量,当绑定套接字时使用。它告诉操作系统自动绑定到所有可用的网络接口的 IPv4 地址。
    sin_port 是 sockaddr_in 结构体的一个成员,用于指定端口号。htons() 函数是 "host to network short" 的缩写,它将一个短整型(16位)从主机字节序转换为网络字节序。
    */

     //绑定套接字
    if(bind(server_fd,(struct sockaddr *)&address,sizeof(address))<0){
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    /*bind() 是一个系统调用,用于将一个套接字(server_fd)绑定到指定的地址和端口上。
    (struct sockaddr *)&address将address变量的地址传递给bind()函数
    其他基本的东西都和上面提到过的大相径庭
    */


    //监听套接字
    if(listen(server_fd,3)<0){//监听,参数3表示最大连接数
      perror("listen");
      exit(EXIT_FAILURE);
    }
    /*listen() 是一个系统调用,用于告诉内核准备接受连接请求。这个函数将一个被动套接字(也称为监听套接字)转变为监听状态。
    参数 3 表示服务器将允许最多3个连接请求在队列中等待接受。
    */

    cout<<"Listening on port"<<PORT<<endl;

    //接受客户端连接
    if((new_socket=accept(server_fd,(struct sockaddr*)&address,(socklen_t*)&addrlen))<0){
        perror("accept");
        exit(EXIT_FAILURE);
    }
    /*accept() 是一个系统调用,用于从监听队列中取出第一个连接请求,并为该请求创建一个新的套接字。*/
    
    //读取客户端发送的数据
    read(new_socket,buffer,1024);//读取数据到缓冲区
    cout<<"消息来自丽宝:"<<buffer<<endl;
    /*read() 是一个系统调用,用于从文件描述符(在这里是套接字)读取数据。
    new_socket是通过 accept() 函数创建的新套接字的文件描述符,用于与客户端进行通信*/

    //发送数据回客户端
    send(new_socket,message,strlen(message),0);//发送消息
    cout<<"消息发送成功\n";

    //关闭套接字
    close(new_socket);//关闭客户端套接字
    close(server_fd);//关闭服务器套接字
    return 0;
}

2.2客户端(client.cpp)

客户端代码中有些在服务端代码中出现过,不清晰的看服务端代码解释就行

#include <iostream>  
#include <sys/socket.h> // 包含socket函数和数据结构
#include <arpa/inet.h> // 包含inet函数,用于IP地址转换
#include <unistd.h>  // 包含标准符号常数和类型
#include <string.h>  

using namespace std;

const int PORT = 1202; // 定义服务器的端口号

int main() {
    int sock = 0; // 定义套接字文件描述符
    struct sockaddr_in serv_addr; // 定义服务器地址结构体
    const char *hello = "赟赟在干嘛!"; // 定义客户端发送的消息
    char buffer[1024] = {0}; // 定义接收数据的缓冲区
    /*sock 用于存储客户端套接字的文件描述符,它是网络通信中的一个重要概念,代表了网络通信的一端。*/
   

    // 创建套接字
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        cout << "\n Socket creation error \n";
        return -1;
    }
    /*如果创建成功,sock 将包含一个非负值;如果创建失败,则为 -1。*/

    serv_addr.sin_family = AF_INET; // 服务器地址族
    serv_addr.sin_port = htons(PORT); // 服务器端口号

    // 将文本形式的IP地址转换为二进制形式
    if (inet_pton(AF_INET, "192.168.47.128", &serv_addr.sin_addr) <= 0) {
        cout << "\nInvalid address / Address not supported \n";
        return -1;
    }
    /*inet_pton() 用于将表示网络地址的字符串转换为网络字节序的二进制形式。*/

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        cout << "\nConnection Failed \n";
        return -1;
    }
    /*connect() 用于客户端套接字与服务器套接字建立连接。*/

    // 发送数据
    send(sock, hello, strlen(hello), 0); // 发送消息
    cout << "消息发送成功:\n";

    // 接收服务器发送的数据
    read(sock, buffer, 1024); // 从服务器接收数据
    cout << "消息来自赟赟: " << buffer << std::endl;

    // 关闭套接字
    close(sock); // 关闭套接字
    return 0;
}

3.测试

在XShell中先执行

g++ -o server server.cpp
g++ -o client client.cpp

选择一个窗口先运行服务器

./server

然后另一个窗口运行客户端

./client

4.简易流程图

开始
服务器创建套接字
套接字创建成功?
设置套接字选项
打印错误并退出
选项设置成功?
绑定套接字到端口
绑定成功?
监听端口
监听成功?
等待客户端连接
接受客户端连接
接受成功?
读取客户端数据
读取成功?
发送数据回客户端
关闭套接字
结束
客户端创建套接字
套接字创建成功?
连接到服务器
连接成功?
发送数据到服务器
接收服务器响应
接收成功?
关闭套接字
### 回答1: Socket网络编程是一种在计算机网络中进行数据交互的编程方式。通过使用Socket,我们可以在不同的计算机之间建立网络连接并进行数据传输。 在C语言中,我们可以使用标准库中的socket函数来创建一个Socket。首先,我们需要使用socket函数来创建一个Socket套接字,指定协议和类型。然后,使用bind函数将Socket与一个本地地址绑定,通常是一个IP地址和一个端口号。接着,使用listen函数启动Socket的监听模式,等待其他计算机发起连接请求。 一旦有其他计算机发起连接请求,我们可以使用accept函数来接受连接,并返回一个新的Socket文件描述符,这个新的Socket可以用于与对方进行数据交互。在这之后,我们可以使用send和recv函数来发送和接收数据。 当数据发送完毕或接收完毕后,我们可以使用close函数关闭Socket,释放资源。在进行Socket网络编程时,我们还可以使用select函数来实现多路复用,同时监听多个Socket,提高程序的效率。 在Socket网络编程中,还可以使用一些其他的函数来设置Socket的各种属性,例如设置Socket为非阻塞模式,设置等待时间等。 总之,Socket网络编程提供了一个方便的方式来进行网络通信。通过使用Socket函数及其相关函数,我们可以在C语言中轻松实现网络编程,实现不同计算机之间的数据交互。 ### 回答2: c socket网络编程是一种用C语言编写的网络编程技术,它允许计算机之间通过网络进行通信和数据交换。在C语言中,socket函数库提供了一种方便的方式来创建网络应用程序。 使用C socket网络编程,我们可以通过创建一个套接字(socket)来建立与其他计算机的连接。套接字可以视为两个应用程序之间的通信端口。一个应用程序可以充当服务器(server),等待其他应用程序的连接请求,并接受它们的连接。另一个应用程序可以充当客户端(client),向服务器发送连接请求,并与服务器建立连接。一旦连接建立,我们可以在客户端和服务器之间传递数据。 在进行C socket网络编程时,我们需要使用一些重要的函数,如socket、bind、listen、accept、connect和send/recv等。其中,socket函数用于创建一个套接字,bind函数用于将套接字与本地IP地址和端口绑定,listen函数用于在服务器端开始监听连接请求,accept函数用于接受客户端的连接请求,connect函数用于与服务器建立连接,send/recv函数用于在已经建立的连接上发送和接收数据。 C socket网络编程在实际应用中具有广泛的用途。它可以用于开发各种类型的网络应用程序,如网络聊天室、网络游戏、文件传输、远程控制等。通过使用C socket网络编程技术,我们可以轻松地实现不同计算机之间的通信和数据交换,为用户提供更好的网络体验。 ### 回答3: C语言的socket网络编程是指利用socket库函数,通过TCP/IP协议或UDP协议在网络上进行数据通信的一种编程方式。 在C语言中,可以使用socket()函数创建一个套接字,该函数返回一个整数值作为套接字的标识符。创建套接字后,可以使用bind()函数将套接字与本地的IP地址和端口号绑定起来。 对于服务器端程序,可以使用listen()函数设置套接字为监听状态,然后通过accept()函数接受客户端的连接请求。 对于客户端程序,可以使用connect()函数连接到服务器端的套接字。连接成功后,客户端可以使用send()函数发送数据给服务器端,也可以使用recv()函数接收服务器端发送过来的数据。 在网络编程中,需要注意错误处理。例如,当创建套接字或者连接失败时,需要使用perror()函数输出错误信息,并且使用close()函数关闭套接字。此外,还需要使用htons()和htonl()函数将主机字节序转换为网络字节序,以及使用ntohs()和ntohl()函数将网络字节序转换为主机字节序。 socket网络编程还可以使用多线程或多进程实现并发处理,可以同时处理多个连接请求,提高服务器的并发性能。 总之,C语言的socket网络编程是一种强大的工具,用于在网络上进行数据通信。它可以使程序与程序之间实现数据的传输和通信,方便地实现分布式系统、网络游戏、即时通信等应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值