网络编程(udp tcp)

news/2025/2/13 4:49:30/

组播通讯:

  1. 发送端实现步骤:
  2. 创建 UDP 类型的套接字
  3. 设置组播地址和组播端口
  4. 向组播地址和组播端口发送数据
  5. 可以接收回复的数据
  6. 关闭套接字
    2.接收端实现步骤:
    1.创建UDP类型的套接字
    2.绑定任意IP,组播端口到套接字上
    3.加入组播组(设置套接字 IPPROTO_IP层 IP_ADD_MEMBERSHIP属性)
struct ip_mreqn mreq;
mreq.imr_multiaddr = xxxx
mreq.imr_address = xxxxx;
mreq.imr_ifindex = if_nametoindex("ens33");
//设置套接字选项
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, 
sizeof(mreq));

4.接收组播数据
5.可以向发送端回复网络数据
6.离开组播组

setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, 
sizeof(mreq));

7.关闭套接字

UDP 广播通讯

广播通讯只能由UDP来完成。广播通讯要借助广播地址。
广播地址:
1. 直接广播地址 (主机ID数据位全为1) 例: 192.168.11.255
2. 本地广播地址 (网络ID和主机ID数据位全为1) 例: 255.255.255.255
广播通讯的流程:
1. 发送端

  1. 创建UDP类型套接字
    2) 设置套接字的广播属性
    setsockopt
    头文件: #include <sys/socket.h>
    函数原型: int setsockopt(int sockfd,int level, int optname ,
    const void *optval,socklen_t optlen);
    函数功能: 接收网络数据
    函数参数: sockfd: 套接字描述符
    level: 指定控制套接字的层次.可以取三种值:
    1) SOL_SOCKET: 通用(应用层)套接
    字选项.
    2) IPPROTO_IP: IP选项(网络层).
    3) IPPROTO_TCP: TCP选项(传输层).
    optname: 待设置的属性名
    optval: 指向属性值的指针
    optlen: 属性值的长度:

函数返回值:成功返回 0
失败返回-1. 错误码存储在errno
例子:
int enable = 1;

setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&enable,sizeof(int));
3) 向广播地址/端口进行数据发送。
4) 接收网络回复数据;
5) 关闭套接字
2. 接收端
1) 创建UDP类型套接字
2) 绑定任意IP/广播端口到套接字上
3) 接收广播数据;
4) 向发送端回复网络数据
5) 关闭套接字

UDP组播通讯

   组播通讯只能由UDP来完成。组播通讯要借助组播地址。

组播地址:D类IP主要用于组播通讯:

        永久组播地址:  224.0.0.1 ~ 224.0.0.255

公用组播地址: 224.0.1.0 ~ 224.0.1.255

函数原型: int listen(int sockl,int backlog);
函数功能: 接收网络数据
函数参数: sockl: 监听套接字描述符
backlog: 可排队连接的最大连接数
函数返回值:成功返回0
失败返回-1. 错误码存储在errno
4. 接受客户端连接,并生成通讯套接字
accept
头文件: #include <sys/socket.h>
函数原型: int accept(int sockl,struct sockaddr* addr,int*
socklen);
函数功能: 接收客户端;连接
函数参数: sockl: 监听套接字描述符
addr: 指向地址结构体指针,用来获取客户端地址信息
socklen: 获取客户端地址信息 结构体长度
函数返回值:成功返回新的套接字。用于与客户端数据收发
失败返回-1. 错误码存储在errno
5. 与客户端进行数据的收发通讯
send
头文件: #include <sys/socket.h>
函数原型: int sendto(int sockfd,void* buf,int len ,int flags);
函数功能: 发送网络数据
函数参数: sockfd: 套接字描述符
buf: 内存缓冲区地址,用于存储待发送的网络数据
len: 待发送的数据长度
flags: 操作标志,一般写为0

函数返回值:成功返回实际发送的数据长度(字节为单位)
失败返回-1. 错误码存储在errno

   recv头文件:    #include <sys/socket.h>

函数原型: int recv(int sockfd,void* buf,int len ,int flags);
函数功能: 接收网络数据
函数参数: sockfd: 套接字描述符
buf: 内存缓冲区地址,用于存储接收到的网络数据
len: 内存缓冲区长度
flags: 操作标志,一般写为0
from: 指向地址结构体的指针,用于接收通讯对方的地址信息:
fromlen: 用于接收对方地址结构体的长度

综合案例

  1. 代码功能概述
    该代码实现了一个简单的广播发送端程序,通过 UDP 协议向指定网络中的广播地址发送用户输入的数据,直到用户输入 “quit” 为止。

  2. 代码结构分析
    头文件和预处理
    #include “header.h”:包含自定义头文件(未提供具体内容),可能定义了 SockAddr、SockAddrIn 等类型。
    缺少标准库头文件:
    需要手动添加以下头文件:

#include <stdio.h> // 用于输入输出(puts、printf)
#include <stdlib.h> // 用于字符串转数字(atoi)
#include <string.h> // 用于字符串操作(strcmp、strlen、memset)
#include <unistd.h> // 用于系统调用(close)
#include <arpa/inet.h> // 用于网络地址转换(inet_addr、htons)
#include <sys/socket.h> // 用于套接字操作(socket、setsockopt、sendto)

主函数
命令行参数检查:

如果参数数量少于 3,输出提示并退出。
第 1 个参数(argv[1]):广播地址(如 192.168.12.255 或 255.255.255.255)。
第 2 个参数(argv[2]):广播端口号。
打印命令行参数:

使用 printf 输出广播地址和端口号。
创建套接字:

使用 socket(AF_INET, SOCK_DGRAM, 0) 创建一个 UDP 套接字。
如果创建失败,输出错误信息并退出。
设置广播模式:

使用 setsockopt 开启套接字的广播模式。
参数说明:
SOL_SOCKET:操作级别(套接字级别)。
SO_BROADCAST:启用广播模式。
&is_enable:设置为 1,表示启用广播。
如果设置失败,输出错误信息并关闭套接字后退出。
配置广播地址:

使用 SockAddrIn 结构体配置广播地址信息。
sin_family:设置为 AF_INET,表示 IPv4 协议。
sin_port:使用 htons 将端口号转换为网络字节序。
sin_addr.s_addr:使用 inet_addr 将广播地址字符串转换为二进制格式。
发送广播数据:

进入一个无限循环,等待用户输入。
使用 fgets 从标准输入读取用户输入的数据。
使用 strlen 删除输入数据中的换行符。
使用 sendto 将数据发送到广播地址。
如果用户输入 “quit”,跳出循环。
使用 memset 清空发送缓冲区。
关闭套接字:

在退出循环后,使用 close 关闭套接字。
3. 代码问题与改进建议
问题
缺少头文件:
代码中未包含必要的头文件,可能导致编译错误。
缓冲区溢出风险:
msg[strlen(msg) - 1] = 0; 如果 msg 为空字符串(如直接回车),会导致数组越界。
广播地址处理不灵活:
代码只支持从命令行参数传入广播地址,未支持默认广播地址(如 255.255.255.255)。
SockAddrIn 定义不明:
代码中使用了 SockAddrIn,但未提供其定义,可能是对 struct sockaddr_in 的别名。
sendto 参数问题:
sendto 的第三个参数 sizeof(msg) 可能导致发送多余的空字节,应使用 strlen(msg)。
改进建议
添加必要的头文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

修复缓冲区溢出问题:

if (msg[strlen(msg) - 1] == '\n')msg[strlen(msg) - 1] = '\0';

支持默认广播地址:

if (strcmp(argv[1], "default") == 0)send_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
elsesend_addr.sin_addr.s_addr = inet_addr(argv[1]);

明确 SockAddrIn 定义:

typedef struct sockaddr_in SockAddrIn;
#include "header.h"
/*
广播的发送端:1.调用setsockopt开启广播模式2.设置广播的特殊IP地址: 192.168.12.255   或者 255.255.255.2553.广播的发送端只做发送*/int main(int argc, char const *argv[])
{// 通过main函数的参数传递ip和端口if (argc < 3){puts("命令行缺少参数!");return -1;}// 打印命令行参数printf("%s--%s\n", argv[1], argv[2]);// 创建发送端的套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket failed");return -1;}// 1.调用setsockopt开启广播模式int is_enable = 1;int r = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &is_enable, sizeof(is_enable));if (-1 == r){perror("setsockopt failed");close(sockfd);return -1;}// 2.设置广播的特殊IP地址: 192.168.12.255   或者 255.255.255.255// 定义地址结构体变量SockAddrIn send_addr;// ip4协议send_addr.sin_family = AF_INET;send_addr.sin_port = htons(atoi(argv[2]));// 192.168.12.255   或者 255.255.255.255send_addr.sin_addr.s_addr = inet_addr(argv[1]);// send_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);  //255.255.255.255// 3.广播的发送端只做发送// 定义要发送的数据char msg[64] = {0};while (1){puts("请输入要发送的内容:");fgets(msg, sizeof(msg), stdin);// 删除最后的换行符msg[strlen(msg) - 1] = 0; //  '\0'// 调用sendto函数发送数据sendto(sockfd, msg, sizeof(msg), 0, (SockAddr *)&send_addr, sizeof(send_addr));// 当用户输入quit是退出发送端if (strcmp("quit", msg) == 0){break;}// 清理数据memset(msg, 0, sizeof(msg)); }// 关闭套接字close(sockfd);return 0;
}
#include "header.h"
/*
广播接收端:1.广播接收端是被动接收,只做接收,不用其他设置
*/
int main(int argc, char const *argv[])
{// 通过命令行传递ip和端口if (argc < 2){puts("命令行缺少参数");return -1;}// 测试打印参数printf("%s\n", argv[1]);// 创建接收端的套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket failed");return -1;}// 定义地址结构体SockAddrIn recv_addr = {0};// ip4协议recv_addr.sin_family = AF_INET;// 端口recv_addr.sin_port = htons(atoi(argv[1]));// ip地址 INADDR_ANY : 0.0.0.0recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip地址是:0.0.0.0 表示任意一个ip地址 //可以通过netstat -tunl 命令查看网络ip地址与端口的占用状态 // 绑定地址int r = bind(sockfd, (SockAddr *)&recv_addr, sizeof(recv_addr));if (-1 == r){perror("bind failed");close(sockfd);return -1;}// 循环接收数据char buf[64] = {0};// 定义广播发送端的地址结构体变量SockAddrIn broadcast_send_addr = {0};ssize_t size = -1;while (1){socklen_t len = sizeof(broadcast_send_addr);size = recvfrom(sockfd, buf, sizeof(buf), 0, (SockAddr *)&broadcast_send_addr, &len);// 打印接收到的数据char *ip = inet_ntoa(broadcast_send_addr.sin_addr);printf("收到%s发送过来的%ld字节是数据是:%s\n", ip, size, buf);// 清空缓冲区memset(buf, 0, sizeof(buf));//清空本次地址信息memset(&broadcast_send_addr, 0, sizeof(broadcast_send_addr));}// 关闭套接字close(sockfd);return 0;
}
  1. 代码功能概述
    该代码实现了一个组播接收端程序,通过加入组播组(如 239.0.0.2)接收组播数据,并实时打印发送者的 IP 地址和接收到的数据。

  2. 代码结构分析
    头文件和预处理
    #include “header.h”:包含自定义头文件(未提供具体内容),可能定义了 SockAddr、SockAddrIn 等类型。
    缺少标准库头文件:
    需要手动添加以下头文件:

#include <stdio.h> // 用于输入输出(puts、printf)
#include <stdlib.h> // 用于字符串转数字(atoi)
#include <string.h> // 用于字符串操作(memset)
#include <unistd.h> // 用于系统调用(close)
#include <arpa/inet.h> // 用于网络地址转换(inet_pton、inet_ntoa、htons)
#include <sys/socket.h> // 用于套接字操作(socket、bind、recvfrom)
#include <net/if.h> // 用于网卡索引操作(if_nametoindex)

主函数
命令行参数检查:

如果参数数量少于 3,输出提示并退出。
第 1 个参数(argv[1]):本机 IP 地址(组播组成员的 IP)。
第 2 个参数(argv[2]):组播端口号。
打印命令行参数:

使用 printf 输出本机 IP 地址和端口号。
创建套接字:

使用 socket(AF_INET, SOCK_DGRAM, 0) 创建一个 UDP 套接字。
如果创建失败,输出错误信息并退出。
绑定套接字:

使用 SockAddrIn 结构体配置绑定地址信息。
sin_family:设置为 AF_INET,表示 IPv4 协议。
sin_port:使用 htons 将端口号转换为网络字节序。
sin_addr.s_addr:设置为 INADDR_ANY,表示接收任意 IP 地址发送的数据。
使用 bind 将地址绑定到套接字。如果绑定失败,输出错误信息并关闭套接字后退出。
加入组播组:

使用 struct ip_mreqn 结构体配置组播组信息。
imr_multiaddr:设置组播组的 IP 地址(239.0.0.2)。
imr_address:设置组播组成员的 IP 地址(从命令行参数获取)。
imr_ifindex:使用 if_nametoindex 获取网卡索引(如 ens33)。
使用 setsockopt 将套接字加入组播组。
接收组播数据:

进入一个无限循环,使用 recvfrom 接收组播数据。
使用 inet_ntoa 解析发送者的 IP 地址。
打印发送者的 IP 地址和接收到的数据。
使用 memset 清空接收缓冲区。
退出逻辑:

程序无退出条件(除非手动终止),因此不会执行到移除组播组和关闭套接字的代码。
3. 代码问题与改进建议
问题
缺少头文件:
代码中未包含必要的头文件,可能导致编译错误。
无限循环:
程序无限循环,无法正常退出,移除组播组和关闭套接字的代码无法执行。
mreqn.imr_ifindex 未处理错误:
if_nametoindex 可能返回 0(表示网卡不存在),但未处理这种情况。
缓冲区溢出风险:
recvfrom 的缓冲区大小为 64 字节,未处理数据超过缓冲区的情况。
SockAddrIn 定义不明:
代码中使用了 SockAddrIn,但未提供其定义,可能是对 struct sockaddr_in 的别名。
改进建议
添加必要的头文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>

添加退出条件:
例如,收到特定消息(如 “quit”)时退出循环。
处理 if_nametoindex 错误:

mreqn.imr_ifindex = if_nametoindex("ens33");
if (mreqn.imr_ifindex == 0) {perror("if_nametoindex failed");close(sockfd);return -1;
}

优化缓冲区处理:
使用动态缓冲区或设置最大接收长度。
明确 SockAddrIn 定义:

typedef struct sockaddr_in SockAddrIn;
#include "header.h"
/*
组播的接收端:1.绑定任意发送端的ip到接收端的套接字上2.添加组播组(设置套接字的选项)         IP_ADD_MEMBERSHIP           struct ip_mreqn mreqn;2.1:设置组播组的ip                 mreqn.imr_multiaddr2.2: 设置组播组成员的ip             mreqn.imr_address2.3: 通过网卡名称获取索引并设置     mreqn.imr_ifindex3.移除组播组 IP_ADD_MEMBERSHIP
*/
int main(int argc, char const *argv[])
{// 通过命令行传递参数if (argc < 3){puts("命令行缺少参数");return -1;}printf("%s---%s\n", argv[1], argv[2]);// 创建套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket failed");return -1;}// 1.绑定任意发送端的ip到接收端的套接字上SockAddrIn multiAddr;multiAddr.sin_family = AF_INET;multiAddr.sin_port = htons(atoi(argv[2]));// 设置接收任意ip地址发送的组播信息multiAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0  通配ip,代表所有ip地址// 绑定地址到当前套接字上int r = bind(sockfd, (SockAddr *)&multiAddr, sizeof(multiAddr));if (-1 == r){perror("bind failed");close(sockfd);return -1;}// 2.添加组播组(设置套接字的选项)// 定义组播相关的结构体struct ip_mreqn mreqn;// 设置组播组的ip地址: 239.0.0.0~239.255.255.255inet_pton(AF_INET, "239.0.0.2", &mreqn.imr_multiaddr);// mreqn.imr_multiaddr.s_addr = inet_addr("239.0.0.2");// 设置组播组成员的ip地址inet_pton(AF_INET, argv[1], &mreqn.imr_address);// 设置网卡的索引//  mreqn.imr_ifindex = 2;mreqn.imr_ifindex = if_nametoindex("ens33");printf("index: %d\n", if_nametoindex("ens33"));// 设置套接字选项setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn));// 接收数据char buf[64] = {0};ssize_t size = -1;// 定义地址结构体,用于接收发送数据的地址信息SockAddrIn sendAddr;while (1){socklen_t len = sizeof(sendAddr);// 接收数据size = recvfrom(sockfd, buf, sizeof(buf), 0, (SockAddr *)&sendAddr, &len);// 解析ip地址char *ip = inet_ntoa(sendAddr.sin_addr);// 打印接收到的数据printf("收到%s发送过来长度为%ld的信息是: %s\n", ip, size, buf);// 清除缓冲数据memset(buf, 0, sizeof(buf));}// 3.移除组播组//  将当前组播中的成员移除setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));// 关闭套接字close(sockfd);return 0;
}
#include "header.h"
/*
组播的发送端:1.设置组播发送端的地址和端口2.发送数据
*/
int main(int argc, char const *argv[])
{// 通过命令传递参数if (argc < 3){puts("命令行缺少参数");return -1;}printf("%s---%s\n", argv[1], argv[2]);// 创建套接字socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket failed");return -1;}// 1.设置组播发送端的地址和端口SockAddrIn multiAddr;multiAddr.sin_family = AF_INET;multiAddr.sin_port = htons(atoi(argv[2]));// multiAddr.sin_addr.s_addr = inet_addr(argv[1]);// multiAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);  //255.255.255.255// 设置ip地址: 可以将字符串按照协议格式转换成32位整数并设置给结构体成员inet_pton(AF_INET, argv[1], &(multiAddr.sin_addr));// 2.发送数据// 定义字符串char msg[64] = {0};while (1){puts("请输入要发送的内容:");fgets(msg, sizeof(msg), stdin);// 去除换行符msg[strlen(msg) - 1] = 0;sendto(sockfd, msg, strlen(msg), 0, (SockAddr *)&multiAddr, sizeof(multiAddr));// 当用户输入quit时退出if (strcmp("quit", msg) == 0){break;}// 清除数据缓冲区memset(msg, 0, sizeof(msg));}// 关闭套接字close(sockfd);return 0;
}
  1. 代码功能概述
    该代码实现了一个简单的 TCP 服务端程序,监听指定 IP 地址和端口,等待客户端连接。客户端连接后,接收客户端发送的数据并回显给客户端。

  2. 代码结构分析
    头文件和预处理
    #include “header.h”:包含自定义头文件(未提供具体内容),可能定义了 SockAddr、SockAddrIn 等类型。
    缺少标准库头文件:
    需要手动添加以下头文件:

#include <stdio.h> // 用于输入输出(puts、printf)
#include <stdlib.h> // 用于字符串转数字(atoi)
#include <string.h> // 用于字符串操作(strlen)
#include <unistd.h> // 用于系统调用(close)
#include <arpa/inet.h> // 用于网络地址转换(inet_addr、inet_ntoa、htons、ntohs)
#include <sys/socket.h> // 用于套接字操作(socket、bind、listen、accept、recv、send)

主函数
命令行参数检查:

如果参数数量少于 3,输出提示并退出。
第 1 个参数(argv[1]):服务端 IP 地址。
第 2 个参数(argv[2]):服务端端口号。
打印命令行参数:

使用 printf 输出服务端 IP 地址和端口号。
创建套接字:

使用 socket(AF_INET, SOCK_STREAM, 0) 创建一个 TCP 套接字。
如果创建失败,输出错误信息并退出。
绑定地址:

使用 SockAddrIn 结构体配置绑定地址信息。
sin_family:设置为 AF_INET,表示 IPv4 协议。
sin_port:使用 htons 将端口号转换为网络字节序。
sin_addr.s_addr:使用 inet_addr 将 IP 地址字符串转换为二进制格式。
使用 bind 将地址绑定到套接字。如果绑定失败,输出错误信息并关闭套接字后退出。
监听客户端:

使用 listen 将套接字设置为监听状态,最大连接数为 2。
如果监听失败,输出错误信息并关闭套接字后退出。
接收客户端连接:

使用 accept 接收客户端的连接。
如果接收失败,输出错误信息并关闭套接字后退出。
成功连接后,打印提示信息。
接收客户端数据:

使用 recv 接收客户端发送的数据。
使用 inet_ntoa 和 ntohs 解析客户端的 IP 地址和端口号。
打印接收到的数据和客户端地址。
发送响应给客户端:

使用 send 将接收到的数据原样返回给客户端。
关闭套接字:

使用 close 关闭客户端和服务端套接字。

/*
TCP网络通讯的服务端
*/
#include "header.h"int main(int argc, char const *argv[])
{if (argc < 3){puts("命令行缺少参数");return -1;}printf("%s---%s\n", argv[1], argv[2]);// 1.创建socketint s_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == s_sockfd){perror("socket failed");return -1;}// 2.绑定自己的地址SockAddrIn serverAddr;// ip4协议serverAddr.sin_family = AF_INET;// 端口  --- arv[2]serverAddr.sin_port = htons(atoi(argv[2]));// ip  -- argv[1]serverAddr.sin_addr.s_addr = inet_addr(argv[1]);// 调用bind函数int r = bind(s_sockfd, (SockAddr *)&serverAddr, sizeof(serverAddr));if (-1 == r){perror("bind failed");close(s_sockfd);return -1;}// 3.监听客户端int r2 = listen(s_sockfd, 2);if (-1 == r){perror("listen failed");close(s_sockfd);return -1;}// 4.接收客户端的连接SockAddrIn clientAddr;socklen_t len = sizeof(clientAddr);// accept有阻塞效果,当和客户端完成三次握手建立连接后,该函数结束阻塞状态,并返回客户端的套接字描述符int c_sockfd = accept(s_sockfd, (SockAddr *)&clientAddr, &len);if (-1 == c_sockfd){perror("accept failed");close(s_sockfd);return -1;}puts("客户端已经和服务端完成了三次握手并连接上了");// 5.接收客户端发送过来的数据char buf[64] = {0};// recv 有阻塞效果,当客户端没有调用send发送数据时,该函数下方的代码不执行ssize_t size = recv(c_sockfd, buf, sizeof(buf), 0);puts("等待客户端发送数据");// 打印接收到的数据和客户端的地址char *ip = inet_ntoa(clientAddr.sin_addr);unsigned short port = ntohs(clientAddr.sin_port);printf("[%s:%d]发送过来了%ld个字节的数据是 %s\n", ip, port, size, buf);// 6.给客户端做出响应:服务端发送数据给客户端send(c_sockfd, buf, strlen(buf), 0);// 7.关闭套接字close(c_sockfd);close(s_sockfd);return 0;
}
  1. 代码功能概述
    该代码实现了一个简单的 TCP 客户端程序,连接到指定的服务端 IP 地址和端口,发送用户输入的数据,并接收服务端的响应。

  2. 代码结构分析
    头文件和预处理
    #include “header.h”:包含自定义头文件(未提供具体内容),可能定义了 SockAddr、SockAddrIn 等类型。
    缺少标准库头文件:
    需要手动添加以下头文件:

#include <stdio.h> // 用于输入输出(puts、printf、fgets)
#include <stdlib.h> // 用于字符串转数字(atoi)
#include <string.h> // 用于字符串操作(strlen)
#include <unistd.h> // 用于系统调用(close)
#include <arpa/inet.h> // 用于网络地址转换(inet_addr、htons)
#include <sys/socket.h> // 用于套接字操作(socket、connect、send、recv)

主函数
命令行参数检查:

如果参数数量少于 3,输出提示并退出。
第 1 个参数(argv[1]):服务端 IP 地址。
第 2 个参数(argv[2]):服务端端口号。
打印命令行参数:

使用 printf 输出服务端 IP 地址和端口号。
创建套接字:

使用 socket(AF_INET, SOCK_STREAM, 0) 创建一个 TCP 套接字。
如果创建失败,输出错误信息并退出。
连接服务端:

使用 SockAddrIn 结构体配置服务端地址信息。
sin_family:设置为 AF_INET,表示 IPv4 协议。
sin_port:使用 htons 将端口号转换为网络字节序。
sin_addr.s_addr:使用 inet_addr 将 IP 地址字符串转换为二进制格式。
使用 connect 连接到服务端。如果连接失败,输出错误信息并关闭套接字后退出。
发送消息:

使用 fgets 从标准输入读取用户输入的数据。
使用 strlen 删除输入数据中的换行符。
使用 send 将数据发送到服务端。
接收服务端响应:

使用 recv 接收服务端发送的数据。
打印服务端的 IP 地址、端口号和响应数据。
关闭套接字:

使用 close 关闭客户端套接字。
3. 代码问题与改进建议
问题
缺少头文件:
代码中未包含必要的头文件,可能导致编译错误。
未处理发送数据错误:
send 可能返回 -1,但未处理这种情况。
未处理接收数据错误:
recv 可能返回 -1(表示错误)或 0(表示服务端断开连接),但未处理这种情况。
缓冲区溢出风险:
msg[strlen(msg) - 1] = 0; 如果 msg 为空字符串(如直接回车),会导致数组越界。
SockAddrIn 定义不明:
代码中使用了 SockAddrIn,但未提供其定义,可能是对 struct sockaddr_in 的别名。

/*
TCP网络通讯的客户端:*/
#include "header.h"int main(int argc, char const *argv[])
{if (argc < 3){puts("命令行缺少参数");return -1;}printf("%s---%s\n", argv[1], argv[2]);// 1.创建套接字int c_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == c_sockfd){perror("socket failed");return -1;}// 2.连接服务器SockAddrIn sockAddr;// ipv4sockAddr.sin_family = AF_INET;// 端口sockAddr.sin_port = htons(atoi(argv[2]));// ip地址sockAddr.sin_addr.s_addr = inet_addr(argv[1]);int r = connect(c_sockfd, (SockAddr *)&sockAddr, sizeof(sockAddr));if (-1 == r){perror("connet failed");close(c_sockfd);return -1;}// 3.发送消息到服务器char msg[64] = {0};puts("请输入要发送的信息:");fgets(msg, sizeof(msg), stdin);// 去除换行符msg[strlen(msg) - 1] = 0;// 发送数据send(c_sockfd, msg, strlen(msg), 0);// 4.接收服务端的信息char data[64] = {0};recv(c_sockfd, data, sizeof(data), 0);printf("[%s:%d]响应回来的信息是 %s\n", argv[1], atoi(argv[2]), data);// 5.关闭套接字close(c_sockfd);return 0;
}

http://www.ppmy.cn/news/1571614.html

相关文章

Lisp语言的软件工程

Lisp语言在软件工程中的应用 引言 在计算机科学的发展历程中&#xff0c;编程语言的演变见证了技术的不断进步与理论的深入探索。Lisp&#xff08;LISt Processing&#xff09;作为一种历史悠久的编程语言&#xff0c;自1958年问世以来&#xff0c;已经走过了超过六十年的风雨…

C# OpenCV机器视觉:颜色量化法提取主色

在热闹繁华的广告设计圈里&#xff0c;阿强是个怀揣无限创意&#xff0c;却总被颜色搭配折腾得焦头烂额的设计师。每次接到项目&#xff0c;面对那些色彩斑斓的素材图片&#xff0c;他就像迷失在彩色迷宫里的小可怜。 “哎呀呀&#xff0c;这么多颜色&#xff0c;到底哪个才是…

【论文笔记】Are Self-Attentions Effective for Time Series Forecasting? (NeurIPS 2024)

官方代码https://github.com/dongbeank/CATS Abstract 时间序列预测在多领域极为关键&#xff0c;Transformer 虽推进了该领域发展&#xff0c;但有效性尚存争议&#xff0c;有研究表明简单线性模型有时表现更优。本文聚焦于自注意力机制在时间序列预测中的作用&#xff0c;提…

华硕笔记本怎么一键恢复出厂系统_华硕笔记本一键恢复出厂系统教程

华硕笔记本怎么一键恢复出厂系统&#xff1f; 华硕一键恢复出厂系统是一个安全、高效、方便的恢复方式&#xff0c;让您轻松还原出厂设置&#xff0c;以获得更好的系统性能。如果您的华硕电脑遇到问题&#xff0c;可以使用华硕一键恢复出厂系统功能。下面小编就教大家华硕笔记本…

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_show_version_info函数

声明 在 nginx.c 开头 static void ngx_show_version_info(void); 实现 static void ngx_show_version_info(void) {ngx_write_stderr("nginx version: " NGINX_VER_BUILD NGX_LINEFEED);if (ngx_show_help) {ngx_write_stderr("Usage: nginx [-?hvVtTq] [-s s…

Expo运行模拟器失败错误解决(xcrun simctl )

根据你的描述&#xff0c;问题主要涉及两个方面&#xff1a;xcrun simctl 错误和 Expo 依赖版本不兼容。以下是针对这两个问题的解决方案&#xff1a; 解决 xcrun simctl 错误 错误代码 72 通常表明 simctl 工具未正确配置或路径未正确设置。以下是解决步骤&#xff1a; 确保 …

Python进阶-在Ubuntu上部署Flask应用

随着云计算和容器化技术的普及&#xff0c;Linux 服务器已成为部署 Web 应用程序的主流平台之一。Python 作为一种简单易用的编程语言&#xff0c;适用于开发各种应用程序。本文将详细介绍如何在 Ubuntu 服务器上部署 Python 应用&#xff0c;包括环境准备、应用发布、配置反向…

机器学习周报-文献阅读

文章目录 摘要abstract 1 文献内容1.1 具有并行架构的混合深度学习模型1.2 时域卷积网络(TCN)1.3 长短期记忆神经网络(LSTM)1.4 Tensor concatenate module 2 实验2.1 数据处理2.2 评估指标2.3 实验结果 3 Savitzky-Golay滤波器(SG filter)4 代码总结 摘要 本周阅读了一篇基于…