网络编程练习题---利用TCP协议完成客户端与服务端点对点通信

cnblogs 2024-06-10 13:15:00 阅读 70

两道练习题:第一道为域名解析函数的使用练习;第二道题是复习使用TCP协议完成服务端与客户端的点对点通信

目录

  • 题目一
    • 解析
    • 代码实现
  • 题目二:
    • 解析
    • 代码实现
      • tcp_client.c
      • tcp_server.c

题目一

设计程序实现解析www.baidu.com的域名,把获取到的百度的IP地址全部输出到终端并验证是否正确。

解析

主机打算响应某个网站的网络请求,但是只知道网站域名是无法通信的,需要对域名进行地址解析,得到网站的公有IP地址,所以Linux系统提供了名称叫做gethostbyname()的函数接口实现该功能,使用规则如下:

image

代码实现

/********************************************************************************

*

*file name:demo_domain.c

*author : ProgramMonkey_Fly@126.com

*date : 2024/06/04

*function :

* 设计程序实现解析www.baidu.com的域名,

* 把获取到的百度的IP地址全部输出到终端并验证是否正确。

* note :

* 1.可以通过ping 百度域名,观察返回值的IP地址来验证获取值是否正确

* 2.通过命令行输入不同的域名,来获取不同域名的IP地址

* 例如:./xxx xxx(有效域名地址)

* version :

*

*CopyRight (c) 2023-2024 ProgramMonkey_Fly@126.com All Right Reseverd

*

* ******************************************************************************/

/****************************头文件*********************************************/

#include <netdb.h>

#include <stdio.h>

#include <errno.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <string.h>

int main(int argc, const char *argv[])

{

//对参数进行错误处理

if(argc != 2)

{

fprintf(stderr, "argc error, errno: %d, %s\n", errno, strerror(errno));

exit(1);

}

//利用gethostbyname函数接口,获取目标域名所包含的信息

struct hostent *hostent = gethostbyname(argv[1]);

//定义一个结构体变量,用于存储IP地址

struct in_addr ip_hostent;

//定义一个循环数组下标

int i = 0;

//循环将获取得到的hostent结构体中的ip地址转换为点分十进制字符串输出

while(hostent->h_addr_list[i] != NULL)

{

//将获取IP地址进行类型转换,存储在ip_hostent中

ip_hostent.s_addr = *(uint32_t *)(hostent->h_addr_list[i]);

//利用函数接口inet_ntoa(),将网络节序类型转换为点分十进制字符串类型并输出

printf("%s ip :%s\n", argv[1], inet_ntoa(ip_hostent));

//对数组下标进行偏移

i++;

}

return 0;

}

题目二:

利用TCP协议,设计两个程序,一个作为客户端,先发送连接申请给服务端,发送消息给服务端;另一个作为服务端,接受客户端申请,并接受客户端发送的消息。

解析

  1. listen函数不具有阻塞特性,因为listen只是相当于把socket的属性更改为被动连接,可以接收其他进程的连接,设置好后便会返回,监听的实质有操作系统完成。
  2. 调用listen函数需要设置监听队列的最大容量,该监听队列容量不是指的是服务端能连接客户端的数量。
  3. accept函数具有阻塞特性,调用一次accept只能接收一个客户端申请请求,并且会生成返回一个新的、专属于当前连接的客户端的文件描述符,该描述符一定要存储起来,方便服务端后续与客户端点对点通信。

代码实现

tcp_client.c

/********************************************************************************

*

*file name:tcp_client.c

*author : ProgramMonkey_Fly@126.com

*date : 2024/06/05

*function : 该案例是掌握TCP协议通信方式,该代码文件将实现客户端发送信息给服务端的功能。

* note :

*1. 需要注意的是connect是非阻塞特性,所以一定要接收返回值进行判断,

* 否则会出现没有连接成功,却进入发送的错误状态。

*2. 由于该函数部分参数由命令行传入,所以在执行文件时要加上参数

* 例如:./xxx xxx(端口号) xxx.xxx.xxx.xxx(服务端IP地址--点分十进制)

* version :

*

*CopyRight (c) 2023-2024 ProgramMonkey_Fly@126.com All Right Reseverd

*

* ******************************************************************************/

/****************************头文件*********************************************/

#include <sys/socket.h>

#include <netinet/in.h>

#include <netinet/tcp.h>

#include <string.h>

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

#include <arpa/inet.h>

#include <unistd.h>

int main(int argc,const char *argv[])

{

//判断命令行参数

if(argc != 3)

{

fprintf(stderr, "argc error,errno:%d,%s\n",errno,strerror(errno));

exit(1);

}

//创建TCP的套接字文件

int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);

if(tcp_socket == -1)

{

fprintf(stderr, "tcp socket error,errno:%d,%s\n",errno,strerror(errno));

exit(2);

}

//绑定本地信息--如果只打算实习发送功能,该步骤也可以省略

struct sockaddr_in host_addr;

host_addr.sin_family = AF_INET; //协议族,为固定宏

host_addr.sin_port = htons(atoi(argv[1]));//想要接收信息进程的端口号

host_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地地址,填写该宏定义代表任意网口都可以接收值

bind(tcp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));

//向目标主机发送消息,需要设置目标端口和目标地址

char buf[128] = {0};

struct sockaddr_in dest_addr;

socklen_t flag_dest = sizeof(dest_addr);

dest_addr.sin_family = AF_INET; //协议族,是固定的

dest_addr.sin_port = htons(atoi(argv[1]));//服务器运行进程的端口号,必须转换为网络字节序

dest_addr.sin_addr.s_addr = inet_addr(argv[2]);//要发送对象的IP地址,必须转换为点分十进制字符串形式

//使用connect函数接口向服务器发起连接申请

int flag_connect = connect(tcp_socket, (struct sockaddr *)&dest_addr, flag_dest);

if(flag_connect == -1)

{

fprintf(stderr, "connect socket error,errno:%d,%s\n",errno,strerror(errno));

exit(3);

}

//循环向服务器发送消息

while(1)

{

//从键盘获取信息

scanf("%s", buf);

write(tcp_socket, buf, strlen(buf));

bzero(buf, sizeof(buf));

}

return 0;

}

tcp_server.c

/********************************************************************************

*

*file name:tcp_server.c

*author : ProgramMonkey_Fly@126.com

*date : 2024/06/17

*function : 该案例是掌握进行模块化编程思想,以及封装函数接口流程

* note :

*1. 需要注意的是listen是非阻塞特性,accept才具有阻塞特性

*2. accpet在成功连接客户端后,会重新创建一个新的、专属于连接客户端的

* 套接字文件描述符,方便服务端与客户端点对点通信

*3. 由于该函数部分参数由命令行传入,所以在执行文件时要加上参数

* 例如:./xxx xxx(端口号)

* version :

*

*CopyRight (c) 2023-2024 ProgramMonkey_Fly@126.com All Right Reseverd

*

* ******************************************************************************/

/****************************头文件*********************************************/

#include <sys/socket.h>

#include <stdio.h>

#include <errno.h>

#include <netinet/ip.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <netinet/udp.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>

int main(int argc, char const *argv[])

{

//对命令行传入参数数量进行判断

if(argc != 2)

{

fprintf(stderr, "argc error,errno:%d,%s\n",errno,strerror(errno));

exit(1);

}

//1.创建TCP套接字

int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);

if (tcp_socket == -1)

{

fprintf(stderr, "tcp socket error,errno:%d,%s\n",errno,strerror(errno));

exit(2);

}

//2.绑定自身的IP地址和端口,由于该处服务器功能是接受信息,所以一定要绑定自身信息

struct sockaddr_in host_addr;

host_addr.sin_family = AF_INET; //协议族,是固定的

host_addr.sin_port = htons(atoi(argv[1]));//目标端口,必须转换为网络字节序

host_addr.sin_addr.s_addr = htonl(INADDR_ANY); //目标地址 INADDR_ANY 这个宏是一个整数,所以需要使用htonl转换为网络字节序

bind(tcp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));

//3.设置监听,且监听队列最大容量设置为5

listen(tcp_socket,5);

//4.等待接受客户端的连接请求

struct sockaddr_in client;

socklen_t client_len = sizeof(client);

//调用accept函数接口后,一定要接收其返回值,因为该返回值为accept创建的新的、专属于连接客户端的套接字文件描述符

int connect_fd = accept(tcp_socket,(struct sockaddr *)&client,&client_len); //会阻塞

char buf[128] = {0};

//5.说明双方建立连接,此时可以接收数据

while(1)

{

read(connect_fd,buf,sizeof(buf));

printf("recv from [%s],data is = %s\n", inet_ntoa(client.sin_addr) ,buf);

bzero(buf,sizeof(buf));

}

return 0;

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。