【Linux】socket网络编程之服务器与客户端的数据交互
前言参考
————————————————
版权声明:本文为CSDN博主「SogK1997」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dive668/article/details/119202871
————————————————
本博文基于作者上一篇博文(【Linux】socket网络编程基础知识),主要用于完善作业。
argc !=2是什么意思
What does argc mean? [duplicate]
argc是参数的数量,并且argv是一个字符串数组。
程序本身是第一个参数argv[0],因此argc总是至少为 1。
那么,argc是2当程序使用一个命令行参数运行。如果它不带参数运行,或者超过一个参数,argc != 2则为真,因此将打印使用消息“Usage: display_image ImageToLoadAndDisplay”,告诉用户如何正确运行它。
inet pton函数
对stdin,stdout 和STDOUT_FILENO,STDIN_FILENO的学习
对stdin,stdout 和STDOUT_FILENO,STDIN_FILENO的学习
Linux网络编程基础知识
Linux下C语言编程
1.首先我们通过如下命令创建一个名为count.c
的文件,并在vim编辑器内,向其中写入C语言程序。
vim count.c
2.用 -o
编译选项来为将产生的可执行文件指定一个文件名来代替 a.out
. 例如, 将一个叫 count.c
的 C 程序编译为名叫 count
的可执行文件, 你将输入下面的命令:
gcc -o count count.c
3.执行count.c
./count
即可运行count
实例:linux下运行hello world
- 创建
hello world.c
vim hello_world.c
- 写入代码如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world for linux!\n");
return 0;
}
- 执行编译
gcc -o hello hello_world.c
- 提示没有找到
gcc
,请install gcc
,通过如下指令install gcc
sudo apt-get update
sudo apt install gcc
- 之后执行编译指令,并通过
ls-l
查看效果
- 执行hello
./hello
成功运行
实验内容和要求
实现以下程序
1.服务器端存放共享变量var_temp,该值从终端(键盘)中输入。
2.多个客户端(数量大于3)不断向服务器端发送数据请求(固定内容"get current var_temp") (ps:此处涉及到并发控制)
3.服务器点接收到客户端请求("get current var_temp")
后,将var_temp
的值减1后发给请求的客户端 (ps:此处是服务器端数据处理并返回)
4.客户端收到服务器端的数据后输出到终端上(printf输出)
5.当服务器端的var_temp
值为0
,向请求的客户端发送 "over"
6.客户端收到"over"
后,退出。
要求:
用C语言编写客户端和服务器端应用程序,输出结果截屏保存到实验报告中。
作业除了提交word版本、PDF版本的报告以外,还需要提交可编译执行的源代码。
完整代码
socket编程在windows和linux下的区别
理解题意
之前没有看要求,就自个去写代码了,实际上,本题是要求:
服务器端存在一个全局的公共变量var temp,并且初始已经有一个确切的值,当客户端向其发送请求,得到这个var temp值减一之后的值,因此如果多台客户端在访问,可能需要做锁机制及相关并发控制。
问题注意
- 要使用strcpy拷贝字符串到字符数组中。
- 要使用stringstream字符串输入输出流,实现
var_temp
的int数字和字符串之间的互相转换(为什么不用itoa
和_itoa
,因为自己bug太多,没有遇到bug的读者可以使用那两个函数)
客户端
/*
* @Description:
* @Author: dive668
* @Date: 2021-07-28 22:39:28
* @LastEditTime: 2021-08-01 20:01:37
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <stdlib.h>
#include <memory.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <stdarg.h>
#include <math.h>
#include <sys/termios.h>
#define MAXLINE 80
#define SERV_PORT 6666
int main(int argc,char *argv[])
{
struct sockaddr_in servaddr; //定义通信进程的地址
char buf[MAXLINE]; //定义缓冲区,用于存放数据
int sockfd,n; //创建两个socket的文件描述符
char var_temp[MAXLINE]; //定义用户字符输入
if(argc!=4) //如果参数数量不为2,即没有带一个参数运行此程序
{
fputs("usage:./programname get current var_temp\n",stderr);
exit(1);
}
else
{
printf("the number of the parameter is right!");
}
strcpy(var_temp,"get current var_temp");
sockfd=socket(AF_INET,SOCK_STREAM,0);//地址类型为AF_INET,连接为TCP,采用默认协议
//用于打开网络通讯端口,调用成功则返回一个文件描述符,调用失败则返回-1
bzero(&servaddr,sizeof(servaddr)); //结构体清零
servaddr.sin_family=AF_INET; //设置地址类型为AF_INET
inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);
servaddr.sin_port=htons(SERV_PORT); //设置端口号为宏定义的端口SERV_PORT
connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
//用于客服端,向服务器端发送请求,类似于bind,调用成功则返回0,否则返回-1
send(sockfd,var_temp,strlen(var_temp),0);
//向处于连接状态的套接字中发送数据
//connfd为接收端的socket⽂件描述符
//buf为要发送的缓冲区中的数据长度
//flag表示调用的执行方式,设为0则可用write代替send函数
n=recv(sockfd,buf,MAXLINE,0);
if(strcmp(buf,"error!")==0)
//如果buf收到的是error!
{
printf("Response error from server\n");
printf("exit...........:\n");
exit(0);
}
else //从已连接的套接字中接收信息,若调用成功则返回读到的字节数
{
printf("Response from server:\n");//write把buf指定的内存写入n个字节,放入到指定的socket文件内
write(STDOUT_FILENO,buf,n);
printf("\n");
printf("%s\n",buf);
close(sockfd);
}
return 0;
}
服务器端
/*
* @Description:
* @Author: dive668
* @Date: 2021-07-29 09:22:52
* @LastEditTime: 2021-08-01 19:40:39
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/termios.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <string>
#include <iostream>
#include <sstream>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <memory.h>
#include <stdarg.h>
#include <math.h>
#define MAXLINE 80
#define SERV_PROT 6666
#define INET_ADDRSTRLEN 16
using namespace std;
int main(void)
{
std::stringstream out;
struct sockaddr_in servaddr,cliaddr; //定义通信进程的地址
socklen_t cliaddr_len; //定义addr的长度
int listenfd,connfd; //创建两个socket的文件描述符
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i,n,var_temp=0;
char temp_string[MAXLINE];
listenfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(SERV_PROT);
bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
listen(listenfd,20);
printf("give the current var_temp:");
scanf("%d",&var_temp);
printf("Accepting connections...\n");
while(1)
{
cliaddr_len=sizeof(cliaddr);
connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
n=recv(connfd,buf,MAXLINE,0);
printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
if(strcmp(buf,"get current var_temp")==0)//strcmp字符串比较
{
if(var_temp!=0)
{
std::stringstream out;
var_temp=var_temp-1;
out<<var_temp;
out>>temp_string;
strcpy(buf,temp_string);
}
else
{
strcpy(buf,"error!");
break;
}
}
else
{
printf("Haven't receive get current var_temp");
}
send(connfd,buf,n,0);
close(connfd);
}
return 0;
}
效果图
服务器端运行效果如下:
客户端运行效果如下: