Linux-并发回应服务器与客户端设计

一.需求与开发环境
利用Linux系统中的socket APIs实现一对服务器与客户端程序,开发工具为VS Code或其他Linux下代码编辑器,编译器为gcc,具体要求如下:
1. 客户端
用户在终端运行客户端程序,命令格式如下:
$>./echoclient
以上命令格式中服务器IP地址不可省略(一般测试时为127.0.0.1);方括号中的重定向可以没有,当没有重定向文件时,用户键盘输入为客户端发送给服务器的数据来源,每当用户按下回车键时,此行输入会被发送至服务端,并在客户端回显(来自服务端的回应),当用户在键盘上按下Ctrl c时,客户端程序结束;当有重定向文件时,客户端将文件中数据逐行发送至服务端,发送完毕且回显完毕结束程序,全过程用户无需键盘输入。
2.服务端
服务器程序在终端中以如下格式后台运行:
$>./echoserver
主进程做好套接字准备后会阻塞在accept上等待客户端请求到达;当客户端请求到达时,服务器主进程需要创建子进程为客户端服务;结束时子进程退出,主进程通过信号处理调用waitpid回收子进程;服务器可以同时处理多个客户端的请求。
二.系统设计
1.系统分析(需要的主要系统调用)
服务器端(server):

Socket()创建一个套接字socket用来监听 Bind()绑定一个IP地址和一个端口 Listen()开启监听,等待接受客户端的连接
Accept()等待处理客户端的连接请求

客户端(client):

Socket()创建一个套接字socket Connect()想服务器发起连接请求

2.系统流程图
在这里插入图片描述
三.系统实现与测试

**新手步骤:
①首先确保虚拟机装有vim编辑器,GCC编译器
②打开terminal(有可视化界面的右键点击打开,无可视化界面的当前界面就是)
③vim client.c ,然后按字母i,输入下面client.c的代码,ESC退出编辑模式。输入冒号+wq(保存)
④同理,vim server.c ,与③相同
⑤用GCC编译器同时编译两个文件。操作:gcc client.c -o client (gcc server.c -o server)
⑥然后同时打开三个terminal,两个运行客户端,一个运行服务端,
⑦文件执行操作: ./client (./server)
⑧注意执行顺序。先打开服务端的执行文件,然后再执行客户端的执行文件
⑨在客户端1和2输入任意文本,服务端都可收到

1.主要代码

client.c

/*file name:client.c*/
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
 
#define _SEVER_IP_ "192.168.1.108"
#define _PORT_ 8080
 
int main()
{
	int sfd,len;
	struct sockaddr_in serv_addr;
	char buf[1024];
 
	sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0){
		perror("socket fail");
		exit(1);
	}
	else{
		printf("client socket success\n");
	}
	bzero(&serv_addr,sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	inet_pton(AF_INET,_SEVER_IP_,&serv_addr.sin_addr.s_addr);
	serv_addr.sin_port = htons(_PORT_);
 
	if(connect(sfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0){
		perror("client conect fail");
		exit(1);
	}
	else{
		printf("client connect success\n");
	}

while(1){
		fgets(buf,sizeof(buf),stdin);
		int r = write(sfd,buf,strlen(buf));
		if(r<0){
			perror("client write fail");
			exit(1);
		}
		printf("write=============== %d\n",r);
		}
	close(sfd);
 
 
	return 0;
}

server.c

/* filename:server.c*/
#include<stdio.h>
#include<string.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<signal.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/socket.h>
 
#define SER_PORT 8080
 
void do_sigchld(int mun)
{
	while(waitpid(0,NULL,WNOHANG)>0);
}
 
int main(void)
{
	
	struct sockaddr_in ser_add,cli_add;
socklen_t cli_add_len;
	char buf[2048];
	char str[128];
	int i,n;
	pid_t pid;
 
	
	sigset_t myset;		
	sigemptyset(&myset);
	sigaddset(&myset,SIGCHLD);
	sigprocmask(SIG_BLOCK,&myset,NULL);
	printf("SIGCHLD is BLOCK.\n");
int listenfd = socket(AF_INET,SOCK_STREAM,0);
	if(listenfd<0){
		perror("socket fail");
		exit(1);
		}
	printf("socket success.\n");
 
	
	int opt = 1;
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	
	
	
	bzero(&ser_add,sizeof(ser_add));
	ser_add.sin_family=AF_INET;
	ser_add.sin_addr.s_addr=htonl(INADDR_ANY);
	ser_add.sin_port=htons(SER_PORT);
	printf("init ser_add success, IP is:%d\t,port is %d\n",ser_add.sin_addr.s_addr,ser_add.sin_port);
 
	int ret =bind(listenfd,(struct sockaddr*)&ser_add,sizeof(ser_add));
	if(ret<0){
		perror("listen fail");
		exit(1);
	}
	printf("bind success\n");
 
	ret = listen(listenfd,20);
	if(ret<0){
		perror("listen fail");
	exit(1);
	}
	printf("listen success\n");
 
	printf("Waiting connections...\n");
	int k=0;
while(1){
cli_add_len = sizeof(cli_add);
 
		int connfd = accept(listenfd,(struct sockaddr*)&cli_add,&cli_add_len);
		if(connfd<0){
			perror("connect fail");
			exit(1);
		}
		else{
			k++;
			printf("创建第 %d 个子进程\n",k);
			pid = fork();
			if(pid<0){
				perror("fork fail\n");
				exit(1);
			}
			else if(pid == 0){
     		close(listenfd);
				while(1){
					int len = read(connfd,buf,sizeof(buf));
					if(len<0){
						perror("read fail");
						exit(1);
					}
					else if(len==0){
						printf("客户端断开连接\n");
						break;
					}
else{
	char str[256];
	printf("客户端的IP:%s\t,端口:%d\n",inet_ntop(AF_INET,&cli_add.sin_addr,str,sizeof(str)),ntohs(cli_add.sin_port));
int len_w=write(STDOUT_FILENO,buf,len);
						if(len_w<0){
							perror("write fail");
							exit(1);
						}
					}
				}
				close(connfd);
				return 0;
			}
			else{
				struct sigaction act;
				act.sa_flags = 0;
				act.sa_handler = do_sigchld;
				sigemptyset(&act.sa_mask);
				sigaction(SIGCHLD,&act,NULL);
				
				sigprocmask(SIG_UNBLOCK,&myset,NULL);
				close(connfd);
			}
		}
	}
return 0;
}	

运行截图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你的动作太慢了!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值