TCP网络编程----C/S模型 (客户端/服务器模型)的代码实现
client发送数据到server,server对数据进行提取并根据用户输入的操作符进行两个数的计算,并将计算结果传给client。(实际上,这个例子已经有了云计算的雏形)
前言
本文章使用的是Ubuntu操作系统和C语言实现了简单的客户端(client)和服务器端(server)的通信。
本文代码实现所需要用到的头文件如下:
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
一、TCP网络编程流程
服务器流程:
1.创建套接字
2.填充服务器的网络信息结构体
3.将套接字和服务器的网络信息结构体绑定
4.把套接字设置成被动监听状态
5.阻塞等待客户端连接
6.收发数据
7.关闭套接字
客户端流程:
1.创建套接字
2.填充服务器的网络信息结构体
3.与服务器建立连接
4.收发数据
5.关闭套接字
二、代码实现
1.服务器代码实现
#include <net_head.h>
/*
功能: 根据客户端传入的数据进行计算(两个数的计算)
参数:
@buf:从客户端接收的数据,存放的buf数组的首地址
@len:数组的长度
返回值: 成功返回计算的结果
*/
int calculate(char* buf, int len)
{
int lvalue = 0;
int rvalue = 0;
char operator;
int i = 0;
int ret = 0;
//(1) 循环找到第一个数字的位置
while ((buf[i] < '0' || buf[i] > '9') && i < len)
i++;
//(2) 从第一个数字的位置往后找一串数字,作为左操作数
for (; buf[i] >= '0' && buf[i] <= '9' && i < len; i++) {
lvalue *= 10;
lvalue += buf[i] - '0';
}
//(3) 循环找运算符
for (; buf[i] != '+' && buf[i] != '-' && buf[i] != '*' && buf[i] != '/' && buf[i] != '%' && i < len; i++) {
printf("%c", buf[i]);
}
operator= buf[i];
//(4) 循环找右操作数开头的第一个数字
while ((buf[i] < '0' || buf[i] > '9') && i < len)
i++;
//(5) 从第一个数字的位置往后找一串数字,作为右操作数
for (; buf[i] >= '0' && buf[i] <= '9' && i < len; i++) {
rvalue *= 10;
rvalue += buf[i] - '0';
}
printf("%d %c %d\n", lvalue, operator, rvalue);
//(6) 根据操作符做运算
switch (operator) {
case '+':
ret = lvalue + rvalue;
break;
case '-':
ret = lvalue - rvalue;
break;
case '*':
ret = lvalue * rvalue;
break;
case '/':
ret = lvalue / rvalue;
break;
case '%':
ret = lvalue % rvalue;
break;
default:
printf("Input error\n");
break;
}
return ret;
}
int main(int argc, const char* argv[])
{
// 1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket error");
exit(-1);
}
// 2.填充服务器的网络信息结构体
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("这里填server端虚拟机的ip地址");
serverAddr.sin_port = htons(这里填任意一个合适的端口号);
// 3.将套接字和服务器的网络信息结构体绑定
socklen_t serverAddr_len = sizeof(serverAddr);
if (-1 == bind(sockfd, (struct sockaddr*)&serverAddr, serverAddr_len)) {
perror("bind error");
exit(-1);
}
// 4.把套接字设置成被动监听状态
if (-1 == listen(sockfd, 5)) {
perror("listen error");
exit(-1);
}
// 5.阻塞等待客户端连接
// int acceptfd = accept(sockfd, (struct sockaddr*)&serverAddr, &serverAddr_len);//error
int acceptfd = accept(sockfd, NULL, NULL);
if (acceptfd == -1) {
perror("accept error");
exit(-1);
}
// 6.接收数据,并作两个数的计算
char buf[128] = "";
int len = sizeof(buf);
read(acceptfd, buf, sizeof(buf));
int ret = calculate(buf, len);
// 7.发送数据
write(acceptfd, &ret, sizeof(ret));
// 8.关闭套接字
close(acceptfd);
close(sockfd);
return 0;
}
2.客户端代码的实现
#include <net_head.h>
int main(int argc, const char* argv[])
{
// 1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket error");
exit(-1);
}
// 2.填充服务器的网络信息结构体
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("这里填server端主机的IP地址");
serverAddr.sin_port = htons(这里填任意一个合适的端口号,eg:7777,server端配置端口转发的时候,也要填这个端口号);
// 3.与服务器简历连接
if (-1 == connect(sockfd, (const struct sockaddr*)&serverAddr, sizeof(serverAddr))) {
perror("connect error");
exit(-1);
}
printf("Connected to server..\n");
// 4.发送数据
char buf[128] = "";
printf("Please input : ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
write(sockfd, buf, sizeof(buf));
// 5.接收数据
int ret = 0;
read(sockfd, &ret, sizeof(ret));
printf("From server : result is >> %d\n", ret);
// 6.关闭套接字
close(sockfd);
return 0;
}
三、两台不同虚拟机之间的通信
注意事项:
本文使用的是VMware软件,对于端口转发的配置,有以下3点注意事项:
1.服务器端要关闭防火墙!
2.client代码中的ip地址要写server端的主机ip(注意不是虚拟机ip)
3.server端所在的主机要配置端口转发:端口号和client代码中的端口号要保持一致):
结果展示
server端
client端: