典型的客户端-服务器架构的简单实现

概要

实现一个简单的客户端-服务器通信模型,基于TCP协议。

  1. 服务器端程序

    • 服务器在指定的端口(SERVER_PORT 8888)上监听客户端的连接请求。
    • 每当有客户端连接到服务器时,服务器通过fork创建一个子进程来处理该客户端的通信。
    • 子进程接收客户端发送的数据,并将其打印到服务器的控制台上。
  2. 客户端程序

    • 客户端程序通过命令行参数指定服务器的IP地址。
    • 客户端尝试连接到服务器,并在连接成功后,从标准输入读取数据,将其发送到服务器。
    • 客户端在发送数据后继续等待用户输入,直到连接断开或发送失败。

实验目的
这两个程序配合使用,展示了TCP通信的基本原理。服务器端程序负责监听和处理多个客户端的连接,而客户端程序负责向服务器发送数据。

实验

环境
Linunx

服务器代码

#include <sys/types.h>          /* 包含基本的数据类型定义 */
#include <sys/socket.h>         /* 包含套接字相关的函数和数据结构定义 */
#include <string.h>             
#include <netinet/in.h>         /* 包含Internet地址族相关的定义 */
#include <arpa/inet.h>          /* 包含IP地址转换的函数定义 */
#include <unistd.h>             /* 包含Unix标准函数定义,如close */
#include <stdio.h>             
#include <signal.h>             /* 包含信号处理相关的定义 */

#define SERVER_PORT 8888        /* 定义服务器监听的端口号 */
#define BACKLOG     10          /* 定义listen函数的监听队列长度 */

int main(int argc, char **argv)
{
	int iSocketServer;          /* 服务器套接字描述符 */
	int iSocketClient;          /* 客户端套接字描述符 */
	struct sockaddr_in tSocketServerAddr;  /* 服务器地址结构 */
	struct sockaddr_in tSocketClientAddr;  /* 客户端地址结构 */
	int iRet;                   /* 用于保存函数返回值 */
	int iAddrLen;               /* 地址结构长度 */

	int iRecvLen;               /* 接收到的数据长度 */
	unsigned char ucRecvBuf[1000]; /* 数据接收缓冲区 */

	int iClientNum = -1;        /* 客户端计数器 */

	/* 忽略子进程的终止信号,防止僵尸进程 */
	signal(SIGCHLD,SIG_IGN);
	
	/* 创建服务器套接字,使用IPv4地址族(AF_INET)和面向连接的TCP协议(SOCK_STREAM) */
	iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == iSocketServer)
	{
		printf("socket error!\n");
		return -1;
	}

	/* 初始化服务器地址结构 */
	tSocketServerAddr.sin_family      = AF_INET;              /* 设置地址族为IPv4 */
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);   /* 设置端口号,并将其转换为网络字节序 */
 	tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;           /* 绑定到所有可用的网络接口 */
	memset(tSocketServerAddr.sin_zero, 0, 8);                 /* 清空sin_zero字段,保持结构体对齐 */
	
	/* 将套接字绑定到指定的IP地址和端口 */
	iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
	if (-1 == iRet)
	{
		printf("bind error!\n");
		return -1;
	}

	/* 监听套接字,准备接受连接请求 */
	iRet = listen(iSocketServer, BACKLOG);
	if (-1 == iRet)
	{
		printf("listen error!\n");
		return -1;
	}

	while (1)  /* 无限循环,持续接受并处理客户端连接 */
	{
		iAddrLen = sizeof(struct sockaddr);
		/* 接受客户端连接,返回客户端套接字描述符 */
		iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
		if (-1 != iSocketClient)
		{
			iClientNum++;  /* 增加客户端计数器 */
			/* 打印客户端IP地址 */
			printf("Get connect from client %d : %s\n",  iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
			if (!fork())  /* 创建子进程处理客户端连接 */
			{
				/* 子进程中的代码 */
				while (1)
				{
					/* 接收客户端发送的数据并显示 */
					iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);
					if (iRecvLen <= 0)  /* 连接关闭或出错 */
					{
						close(iSocketClient);  /* 关闭客户端套接字 */
						return -1;  /* 结束子进程 */
					}
					else
					{
						ucRecvBuf[iRecvLen] = '\0';  /* 添加字符串结束符 */
						/* 打印接收到的客户端消息 */
						printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);
					}
				}				
			}
		}
	}
	
	/* 关闭服务器套接字 */
	close(iSocketServer);
	return 0;
}

客户端代码

#include <sys/types.h>          /* 包含基本的数据类型定义 */
#include <sys/socket.h>         /* 包含套接字相关的函数和数据结构定义 */
#include <string.h>             
#include <netinet/in.h>         /* 包含Internet地址族相关的定义 */
#include <arpa/inet.h>          /* 包含IP地址转换的函数定义 */
#include <unistd.h>             /* 包含Unix标准函数定义,如close */
#include <stdio.h>              

#define SERVER_PORT 8888        /* 定义服务器监听的端口号 */

int main(int argc, char **argv)
{
	int iSocketClient;          /* 客户端套接字描述符 */
	struct sockaddr_in tSocketServerAddr;  /* 服务器地址结构 */
	
	int iRet;                   /* 用于保存函数返回值 */
	unsigned char ucSendBuf[1000]; /* 数据发送缓冲区 */
	int iSendLen;               /* 发送的数据长度 */

	/* 检查命令行参数,必须提供服务器IP地址 */
	if (argc != 2)
	{
		printf("Usage:\n");
		printf("%s <server_ip>\n", argv[0]);
		return -1;
	}

	/* 创建客户端套接字,使用IPv4地址族(AF_INET)和面向连接的TCP协议(SOCK_STREAM) */
	iSocketClient = socket(AF_INET, SOCK_STREAM, 0);

	/* 初始化服务器地址结构 */
	tSocketServerAddr.sin_family      = AF_INET;              /* 设置地址族为IPv4 */
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);   /* 设置端口号,并将其转换为网络字节序 */
 	//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;         /* 使用INADDR_ANY时,将绑定到所有可用的接口 */
 	
	/* 将命令行提供的IP地址字符串转换为网络字节序的二进制形式 */
	if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
 	{
		printf("invalid server_ip\n");  /* 如果转换失败,提示IP地址无效 */
		return -1;
	}
	memset(tSocketServerAddr.sin_zero, 0, 8);  /* 清空sin_zero字段,保持结构体对齐 */

	/* 连接到服务器 */
	iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
	if (-1 == iRet)
	{
		printf("connect error!\n");  /* 如果连接失败,打印错误信息并退出 */
		return -1;
	}

	/* 循环从标准输入读取数据并发送到服务器 */
	while (1)
	{
		if (fgets(ucSendBuf, 999, stdin))  /* 从标准输入读取一行数据 */
		{
			iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);  /* 发送数据到服务器 */
			if (iSendLen <= 0)  /* 如果发送失败或连接断开 */
			{
				close(iSocketClient);  /* 关闭客户端套接字 */
				return -1;  /* 退出程序 */
			}
		}
	}
	
	return 0;
}

实验结果

//客户端消息 
ktj:~/tcp$ ./client.out  127.0.0.1
hello world ktj


//服务器端消息
ktj:~/tcp$ ./server.out  
Get connect from client 0 : 127.0.0.1
Get Msg From Client 0: hello world ktj


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值