Linux网络编程(TCP Socket编程实现过程)

一、TCP Socket Server 服务器

二、TCP Socket Client 客户端

一、TCP Socket Server 服务器

基本流程框架

在这里插入图片描述

1. 创建服务器套接字Socket

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

AF_INET      IPv4 Internet protocols                    ip(7)

SOCK_STREAM     Provides sequenced, reliable, two-way, connection-based
                       byte  streams.  An out-of-band data transmission mecha‐
                       nism may be supported.

RETURN VALUE
       On success, a file descriptor for the new socket is returned.   On  er‐
       ror, -1 is returned, and errno is set appropriately.

配置参数(网络模型、协议)

  • 英特尔网IPv4
  • TCP协议用到SOCK_STREAM
  • protocol协议书

判断返回值

  • 成功后,将返回新套接字的文件描述符。
  • 出错时,返回 -1,并适当设置 errno。

实现 创建服务器套接字 功能点

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

int main()
{
    int server_socket;

    // 创建服务器套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (server_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }
    
    // 设置服务器地址结构

    // 绑定服务器套接字

    // 监听传入的连接请求

    // 接受客户端连接请求

    // 从客户端接收数据

    // 向客户端发送数据

    // 关闭套接字

    return 0;
}

2. 设置服务器地址结构htons、inet_aton和inet_ntoa

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

配置参数

struct sockaddr_in替换struct sockaddr
/usr/include搜索struct sockaddr_in

cd /usr/include/

在这里插入图片描述

grep找到对应的struct sockaddr_in位置

grep "struct sockaddr_in {" * -nir

这条命令是在Linux/Unix操作系统中使用grep工具来搜索包含字符串 “struct sockaddr_in {” 的文件。具体含义如下:

  • grep: 这是用于搜索文本的命令行工具。
  • "struct sockaddr_in {": 这是要搜索的字符串,它包含了结构体定义的一部分。
  • *: 这表示搜索所有文件,不仅限于当前目录,而是包括所有子目录。
  • -n: 这是grep的一个选项,用于显示匹配行的行号
  • -i: 这是grep的一个选项,用于执行大小写不敏感的搜索,即不区分大小写
  • -r: 这是grep的一个选项,用于递归地搜索子目录

因此,这条命令的含义是在当前目录及其所有子目录中,搜索包含字符串 “struct sockaddr_in {” 的文件,并显示匹配行的行号。这通常用于查找包含特定结构体或关键字的源代码文件,以便进行源代码分析或修改。
在这里插入图片描述

最后在通过vim编辑器找到文件中申明定义的位置

vim linux/in.h +261
vim +对应的位置的头文件 +行号

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

/* Structure describing an Internet (IP) socket address. */
#if  __UAPI_DEF_SOCKADDR_IN
#define __SOCK_SIZE__   16      /* sizeof(struct sockaddr)  */
struct sockaddr_in {
  __kernel_sa_family_t  sin_family; /* Address family       */
  __be16        		sin_port;   /* Port number          */
  struct in_addr    	sin_addr;   /* Internet address     */

  /* Pad to size of `struct sockaddr'. */
  unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
            sizeof(unsigned short int) - sizeof(struct in_addr)];
};
#define sin_zero    __pad       /* for BSD UNIX comp. -FvK  */
#endif

这段代码定义了一个结构体 struct sockaddr_in,用于描述一个 Internet (IP) 套接字地址。这是在网络编程中经常使用的结构,通常用于指定套接字的地址信息。下面是该结构体的成员及其作用:

  • sin_family:一个表示地址家族(Address family)的字段。这个字段指定了套接字的地址类型,例如 IPv4 或 IPv6。在这个结构中,sin_family 是一个类型为 __kernel_sa_family_t 的字段,用于表示地址家族。
  • sin_port一个表示端口号的字段。这个字段指定了套接字的端口号,用于标识应用程序在主机上的通信端口。
  • sin_addr一个结构体,表示 Internet 地址。通常,它包含 IP 地址信息,用于指定要连接的远程主机的地址。
  • sin_zero:用于兼容 BSD UNIX 计算机的字段,通常不需要在现代系统中使用。

此外,结构体中还包含了一个 __pad 字段,用于填充结构体的大小,使其与 struct sockaddr 相匹配。__pad 用于在不同系统上确保结构体的大小一致,以便在网络编程中正确传递套接字地址。

这个结构体通常在网络编程中用于指定要连接的远程服务器的地址,其中 sin_family 指定了地址类型,sin_port 指定了端口号,sin_addr 包含了远程主机的 IP 地址。这允许程序建立与远程主机的连接。

配置第二个参数端口号的字段,要调用字节转换API

因为X86系统CPU是小端字节序,网络是大段字节序
所以要将16位主机字节序数据转换为网络字节序

       #include <arpa/inet.h>
            
       uint16_t htons(uint16_t hostshort);
       
       uint32_t htonl(uint32_t hostlong);
       
       uint16_t ntohs(uint16_t netshort);

       uint32_t ntohl(uint32_t netlong);

uint16_t htons(uint16_t host16bitvalue)

  • 函数名含义:htons代表"host to network short",即将主机字节序的16位整数转换为网络字节序。
  • 参数:host16bitvalue是一个16位整数,表示主机字节序的值。
  • 返回值:函数返回一个16位整数,表示网络字节序的值。
配置第三个参数sin_addr是结构体struct in_addr里面的变量

在这里插入图片描述

#if __UAPI_DEF_IN_ADDR
/* Internet address. */
struct in_addr {
    __be32  	s_addr;
};
#endif
调用inet_aton和inet_ntoa函数 (地址转换)
inet_aton 函数:把字符串形式的“192.168.1.123”转为网络能识别的格式
int inet_aton(const char *cp, struct in_addr *inp);
inet_aton 函数用于将一个字符串表示的IPv4地址转换为二进制形式的IPv4地址。

inet_ntoa 函数:把网络格式的ip地址转为字符串形式
char *inet_ntoa(struct in_addr in);
inet_ntoa 函数用于将一个二进制形式的IPv4地址转换为字符串表示的IPv4地址。

实现 设置服务器地址结构 功能点

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int server_socket;
    struct sockaddr_in server_addr;

    // 创建服务器套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (server_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//    server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("172.168.1.1", &server_addr.sin_addr);

    // 绑定服务器套接字

    // 监听传入的连接请求

    // 接受客户端连接请求

    // 从客户端接收数据

    // 向客户端发送数据

    // 关闭套接字

    return 0;
}

3. 绑定服务器套接字Bind

调用bind函数API

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

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

bind 函数用于将一个套接字绑定到一个特定的地址和端口号,以便在该地址上监听传入的连接。下面是 bind 函数的详细信息:

  • 功能bind 函数用于将指定的套接字与特定的网络地址和端口绑定在一起,从而将套接字关联到指定的通信地址。这通常用于服务器端创建监听套接字。

  • 参数

    1. sockfd:要绑定的套接字的文件描述符。
    2. addr:一个指向 struct sockaddr 结构体的指针,其中包含了要绑定的地址信息。这个结构体的具体类型(struct sockaddr_instruct sockaddr_in6)取决于套接字的地址族(address family)。
    3. addrlenaddr 结构体的大小(以字节为单位)。
  • 返回值

    • 如果 bind 函数成功,它会返回0。
    • 如果发生错误,它会返回-1,并设置全局变量 errno 以指示错误类型。
  • 注意事项

    • 要成功地使用 bind,套接字必须事先创建,通常是使用 socket 函数创建的。
    • addr 结构体的内容应该与套接字的地址族和类型匹配。例如,如果使用 AF_INETSOCK_STREAM 创建的套接字,addr 应该是 struct sockaddr_in 类型,并包含IPv4地址和端口号。
    • 通常,服务器端在调用 bind 后还需要调用 listen 函数,以开始监听传入的连接请求。

bind 函数在服务器端网络编程中非常重要,因为它允许服务器指定要监听的地址和端口,以等待客户端的连接请求。通过将套接字绑定到特定地址和端口,服务器可以确保客户端可以找到并与之通信。

实现 绑定服务器套接字 功能点

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int server_socket;
    struct sockaddr_in server_addr;

    // 创建服务器套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (server_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//    server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("172.168.1.1", &server_addr.sin_addr);

    // 绑定服务器套接字
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Binding failed");
        exit(1);
    }

    // 监听传入的连接请求

    // 接受客户端连接请求

    // 从客户端接收数据

    // 向客户端发送数据

    // 关闭套接字

    return 0;
}

4. 监听传入的连接请求Listen

调用listen函数

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

int listen(int sockfd, int backlog);

listen 函数用于在服务器端创建监听套接字,主要用于准备接受客户端的连接请求。它有以下功能:

  1. 功能:

    • listen 函数用于告知操作系统,套接字(通常是服务器套接字)已准备好接受客户端的连接请求。一旦调用 listen,套接字进入监听状态,可以接受连接请求。
    • listen 函数还通过参数 backlog 来指定了等待连接的最大队列长度。这个参数表示可以排队等待服务的客户端连接的数量上限,即在未连接队列中可以排队等待的连接请求数。
  2. 内核维护的两个队列:

    • 已连接队列(Completed Connection Queue):已经建立连接的客户端连接会进入这个队列,等待服务器接受。
    • 未连接队列(Incomplete Connection Queue):尚未完全建立连接的客户端请求会进入这个队列,等待服务器调用 accept 函数来接受连接。

    在未连接队列中,操作系统会保存客户端的连接请求,这些请求还未完成TCP的三次握手过程。这允许服务器先接受连接请求,然后再完成握手。

  3. TCP的三次握手:

    • 在服务器调用 listen 函数之后,它可以开始接受客户端连接请求。
    • 当客户端尝试连接到服务器时,它发送一个SYN(同步)请求,表示建立连接。这个请求进入未连接队列。
    • 服务器接受到这个请求后,向客户端发送一个SYN-ACK(同步-确认)响应,表示愿意建立连接。此时,连接请求从未连接队列移动到已连接队列。
    • 最后,客户端收到服务器的SYN-ACK响应后,发送一个ACK(确认),确认连接的建立。此时,TCP的三次握手完成,连接正式建立,可以开始数据传输。

listen 函数的作用是告知操作系统,服务器套接字已准备好接受客户端连接请求,并通过指定 backlog 参数来控制未连接队列中等待连接的客户端连接数上限。一旦连接请求到达服务器,它们将会排队等待在未连接队列中,直到服务器接受连接或达到 backlog 上限。

这个机制允许服务器在接受连接之前排队等待连接请求,同时处理其他任务。当连接完成三次握手后,连接进入已连接队列,服务器可以继续与客户端通信。

  1. 参数:
    • sockfd:这是一个已创建的套接字描述符,通常是一个服务器套接字,已经绑定到服务器地址和端口上,用于监听客户端的连接请求。
    • backlog:这是一个整数值,表示等待连接队列的最大长度,也就是可以排队等待服务的客户端连接的数量上限。这个参数决定了在未连接队列中能够排队的连接数上限。

总之,listen 函数告知操作系统,服务器套接字已准备好接受客户端连接请求,同时通过 backlog 参数来控制未连接队列中等待连接的客户端连接数上限。一旦连接请求到达服务器,它们将会排队等待在未连接队列中,直到服务器接受连接或达到 backlog 上限。

实现 监听传入的连接请求 功能点

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int server_socket;
    struct sockaddr_in server_addr;

    // 创建服务器套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (server_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//    server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("172.168.1.1", &server_addr.sin_addr);

    // 绑定服务器套接字
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Binding failed");
        exit(1);
    }

    // 监听传入的连接请求
    if (listen(server_socket, 5) == 0) {
        printf("Listening for incoming connections...\n");
    } else {
        perror("Listening failed");
        exit(1);
    }

    // 接受客户端连接请求

    // 从客户端接收数据

    // 向客户端发送数据

    // 关闭套接字

    return 0;
}

5. 接受客户端连接请求Accept

调用accept函数

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

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

accept 函数是用于接受客户端连接请求的套接字函数。它的功能、参数和返回值如下:

功能:

  • accept 函数用于从已连接队列中接受客户端连接请求,创建一个新的套接字,用于与客户端进行通信。一旦接受连接,服务器和客户端之间的数据传输就可以开始。

参数:

  • sockfd:这是一个监听套接字(通常是服务器套接字),它已经通过 socketbindlisten 函数准备好接受客户端连接。
  • addr:这是一个指向 struct sockaddr 类型的指针,用于存储客户端的地址信息。通常,可以将其设置为 NULL,表示不需要获取客户端地址。
  • addrlen:这是一个整数,表示 addr 结构体的长度,通常是 sizeof(struct sockaddr)

返回值:

  • 如果成功,accept 函数返回一个新的套接字描述符,该描述符用于与客户端通信。这个新套接字是一个专门用于连接到客户端的套接字,它是唯一的。
  • 如果发生错误,accept 返回 -1,并设置 errno 变量以指示错误的类型。

一旦 accept 函数成功,服务器可以使用返回的新套接字与客户端建立通信。这个新套接字是一个全双工的通信通道,允许数据在服务器和客户端之间双向传输。

通常,服务器会在循环中多次调用 accept 函数,以便接受多个客户端的连接请求。每次调用 accept 都会返回一个新的套接字,用于处理与一个客户端的通信。这使得服务器能够同时与多个客户端建立连接,并提供并发服务。

连接到本地

ifconfig
inet 192.168.1.145

ping 一下网络看看是否能连通

在这里插入图片描述

telnet 网络协议

Telnet 是一种网络协议,用于在远程计算机之间建立交互式文本通信会话。它允许用户通过网络连接到远程主机,就像他们直接坐在目标计算机的终端上一样。用户可以在 Telnet 会话中执行命令、查看远程主机的输出并与远程系统进行交互。

以下是 Telnet 的一些关键特点和用途:

  1. 远程控制: Telnet 允许用户从本地计算机远程连接到其他计算机,以执行命令、管理文件和资源,甚至配置网络设备。

  2. 文本协议: Telnet 会话是基于文本的,用户通过纯文本输入和输出进行通信。这使得 Telnet 在控制和监控远程设备、服务器和网络设备方面非常有用。

  3. 端口号: Telnet 默认使用 TCP 端口 23。用户可以通过指定目标计算机的 IP 地址和端口号来建立 Telnet 连接。

  4. 非加密: Telnet 是一种不加密的协议,这意味着通信数据在传输过程中是明文的,不安全。因此,在互联网上使用 Telnet 时,应格外小心,特别是在涉及敏感信息或登录凭据时。

  5. 调试和远程访问: Telnet 常用于远程服务器的调试、系统管理和故障排除。管理员可以通过 Telnet 连接到远程服务器,检查系统状态并执行必要的操作。

  6. 替代协议: 由于 Telnet 的不安全性,SSH(Secure Shell)已经替代了 Telnet 作为更安全的远程访问协议。SSH 提供了加密通信和更高级别的安全性功能。

虽然 Telnet 仍然存在,但在大多数情况下,特别是在公共网络中,使用 SSH 或其他加密协议是更安全的选择,以保护敏感信息免受未经授权的访问。但在某些特定情况下,Telnet 仍然是一个有用的工具,例如在内部网络中用于设备管理和调试。
在这里插入图片描述

telnet 192.168.1.145 12345

在这里插入图片描述

实现 接受客户端连接请求 功能点

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);

    // 创建服务器套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (server_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//    server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("192.168.1.145", &server_addr.sin_addr);

    // 绑定服务器套接字
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Binding failed");
        exit(1);
    }

    // 监听传入的连接请求
    if (listen(server_socket, 10) == 0) {
        printf("Listening for incoming connections...\n");
    } else {
        perror("Listening failed");
        exit(1);
    }

    // 接受客户端连接请求
    client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);

    if (client_socket < 0) {
        perror("Acceptance failed");
        exit(1);
    }

    // 从客户端接收数据

    // 向客户端发送数据

    // 关闭套接字

    printf("connect\n");
    while (1);

    return 0;
}

6. 服务器数据的收发Recv、Send

从客户端接收数据

recv 函数是用于从套接字接收数据的函数。它通常用于网络编程中,从网络连接中读取数据。以下是 recv 函数的详细信息:

函数原型:

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

参数:

  • sockfd:这是一个已经建立连接的套接字,用于接收数据。
  • buf:这是一个指向存储接收数据的缓冲区的指针。
  • len:这是要接收的数据的最大字节数,通常是缓冲区的大小。
  • flags:这是一个用于指定接收选项的整数。常见的选项包括 0(无选项)和 MSG_WAITALL(阻塞接收,等待接收完整的数据包)等。

返回值:

  • 如果成功,recv 函数返回接收到的数据的字节数。可以小于 len,表示接收的数据不满足请求的字节数。
  • 如果连接已关闭,recv 返回 0,表示对端已关闭连接。
  • 如果发生错误,recv 返回 -1,并设置 errno 变量以指示错误的类型。

功能:

  • recv 函数用于从套接字接收数据,将数据从网络中读取到缓冲区中。这个数据可以是来自另一台计算机的数据,也可以是同一台计算机上的本地套接字的数据。接收的数据可以包含任何字节,无论是文本数据还是二进制数据。
  • recv 函数通常是阻塞的,即它将等待直到接收到数据或发生错误。但可以通过 flags 参数将套接字设置为非阻塞模式,以使 recv 函数在没有数据可接收时立即返回。

在网络编程中,recv 函数通常用于从套接字接收来自网络连接的数据。客户端使用 recv 函数从服务器接收响应数据,服务器使用 recv 函数从客户端接收请求数据。这允许双方在网络上进行数据交换,实现各种通信和应用。

从客户端接收数据 功能点
	// 从客户端接收数据
    recv(client_socket, buffer, sizeof(buffer), 0);
    printf("Client says: %s\n", buffer);

向客户端发送数据

send 函数是用于将数据从套接字发送到目标的函数。它通常用于网络编程中,将数据从客户端发送到服务器,或者从服务器发送数据给客户端。以下是 send 函数的详细信息:

函数原型:

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

参数:

  • sockfd:这是一个已经建立连接的套接字,用于发送数据。
  • buf:这是一个指向包含要发送数据的缓冲区的指针。
  • len:这是要发送的数据的字节数。
  • flags:这是一个用于指定发送选项的整数。常见的选项包括 0(无选项)和 MSG_DONTWAIT(非阻塞发送)等。

返回值:

  • 如果成功,send 函数返回已发送数据的字节数,可以小于 len,表示未能发送完所有数据。
  • 如果发生错误,send 返回 -1,并设置 errno 变量以指示错误的类型。

功能:

  • send 函数用于将数据从套接字发送到目标。这个目标可以是另一台计算机上的套接字,也可以是同一台计算机上的本地套接字。发送的数据可以包含任何字节,无论是文本数据还是二进制数据。
  • send 函数通常是阻塞的,即它将等待直到所有数据发送完毕或发生错误。但可以通过 flags 参数将套接字设置为非阻塞模式,以使 send 函数在无法发送数据时立即返回。

在网络编程中,send 函数通常用于将数据发送到套接字,以便在服务器和客户端之间进行通信。客户端使用 send 函数将请求发送给服务器,服务器使用 send 函数将响应发送回客户端。这允许双方在网络上进行数据交换,实现各种通信和应用。

向客户端发送数据 功能点

    // 向客户端发送数据
    send(client_socket, message, sizeof(message), 0);
    printf("Message sent to client.\n");

7. 关闭套接字Close

#include <unistd.h>
    // 关闭套接字
    close(server_socket);
    close(client_socket);

代码实现服务器代码实现服务器

my_socket.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main()
{
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);

    // 创建服务器套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (server_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//  server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("192.168.1.145", &server_addr.sin_addr);

    // 绑定服务器套接字
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Binding failed");
        exit(1);
    }

    // 监听传入的连接请求
    if (listen(server_socket, 10) == 0) {
        printf("Listening for incoming connections...\n");
    } else {
        perror("Listening failed");
        exit(1);
    }

    // 接受客户端连接请求
    client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);

    if (client_socket < 0) {
        perror("Acceptance failed");
        exit(1);
    }

    // 从客户端接收数据
    recv(client_socket, buffer, sizeof(buffer), 0);
    printf("Client says: %s\n", buffer);

    // 向客户端发送数据
    send(client_socket, message, sizeof(message), 0);
    printf("Message sent to client.\n");    

    // 关闭套接字
    close(server_socket);
    close(client_socket);

    return 0;
}

在这里插入图片描述

二、TCP Socket Client 客户端

基本流程框架

在这里插入图片描述

1. 创建客户端套接字Socket

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

int main()
{
    int client_socket;

    // 创建客户端套接字
    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (client_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构

    // 连接到服务器

    // 向服务器发送数据

    // 从服务器接收数据

    // 关闭套接字

    return 0;
}

2. 设置服务器地址结构htons、inet_aton和inet_ntoa

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int client_socket;
    struct sockaddr_in server_addr;

    // 创建客户端套接字
    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (client_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//  server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("192.168.1.145", &server_addr.sin_addr);

    // 连接到服务器

    // 向服务器发送数据

    // 从服务器接收数据

    // 关闭套接字

    return 0;
}

3. 连接到服务器Connect

connect 函数是用于在客户端建立与服务器的连接的函数。它通常用于网络编程中,用于客户端套接字连接到服务器套接字。以下是 connect 函数的详细信息:

函数原型:

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

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

参数:

  • sockfd:这是客户端套接字的文件描述符,用于建立连接。
  • addr:这是一个指向服务器地址信息的结构体指针,通常是 struct sockaddr 的指针,其中包含了服务器的 IP 地址和端口号等信息。
  • addrlen:这是服务器地址信息的长度,通常是 sizeof(struct sockaddr)

返回值:

  • 如果成功,connect 函数返回 0,表示连接已建立。
  • 如果发生错误,connect 返回 -1,并设置 errno 变量以指示错误的类型。

功能:

  • connect 函数用于在客户端建立与服务器的连接。客户端将其套接字连接到服务器的套接字,以便进行数据交换。一旦连接建立,客户端和服务器之间可以相互发送和接收数据。
  • connect 函数通常是阻塞的,即它将等待直到连接成功或发生错误。连接成功后,客户端可以使用该套接字向服务器发送请求,并接收服务器的响应。

在网络编程中,connect 函数是客户端建立与服务器通信的第一步。客户端使用 connect 函数连接到服务器,然后可以使用 send 函数发送请求,使用 recv 函数接收响应。这允许客户端与服务器之间进行双向通信,实现各种网络应用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main()
{
    int client_socket;
    struct sockaddr_in server_addr;

    // 创建客户端套接字
    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (client_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//  server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("192.168.1.145", &server_addr.sin_addr);

    // 连接到服务器
    if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == 0) {
        printf("Connected to server.\n");
    } else {
        perror("Connection failed");
        exit(1);
    }

    // 向服务器发送数据

    // 从服务器接收数据

    // 关闭套接字

    return 0;
}

4. 客户端数据的收发Send、Recv

向服务器发送数据

    // 向服务器发送数据
    send(client_socket, message, sizeof(message), 0);
    printf("Message sent to server.\n");

从服务器接收数据

    // 从服务器接收数据
    recv(client_socket, buffer, sizeof(buffer), 0);
    printf("Server says: %s\n", buffer);

5. 关闭套接字Close

    // 关闭套接字
    close(client_socket);

代码实现客户端

my_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main()
{
    int client_socket;
    struct sockaddr_in server_addr;
    char message[] = "Hello, server!";
    char buffer[1024];

    // 创建客户端套接字
    client_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (client_socket == -1) {
        perror("Socket creation failed");
        exit(1);
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
//  server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton("192.168.1.145", &server_addr.sin_addr);

    // 连接到服务器
    if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == 0) {
        printf("Connected to server.\n");
    } else {
        perror("Connection failed");
        exit(1);
    }

    // 向服务器发送数据
    send(client_socket, message, sizeof(message), 0);
    printf("Message sent to server.\n");

    // 从服务器接收数据
    recv(client_socket, buffer, sizeof(buffer), 0);
    printf("Server says: %s\n", buffer);

    // 关闭套接字
    close(client_socket);

    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱年糕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值