• 首页

  • 官方

  • 主题

  • 关注

  • 联系

网络编程:实现多进程并发回声服务器端/客户端

网络编程:实现多进程并发回声服务器端/客户端

1.直接跳转到Linux端代码


一、实验目的

  1. 学习进程的创建、销毁过程。掌握利用信号处理技术sigaction消灭僵尸进程的方法。
  2. 在Linux操作系统上编写并发服务器端/客户端。让服务器端以多进程方式为多个客户端同时提供回声服务。

二、实验内容

1、在Linux操作系统上编写程序,实现基于多进程的并发回声服务器端:
(1)改进实验三中实现的迭代服务器端程序。通过为每一个客户端创建进程的方式,同时为多个客户端提供回声服务。
(2)客户端接收用户输入的字符串并发送到服务器端,一直到用户输入字符 ”Q”/”q” 为止。
(3)启动服务器后创建两个以上客户端并建立连接,验证服务器端可以同时为不同的客户端提供回声服务。
2、回声客户端采用I/O程序分割方法,发送的消息记录到文件中

实验结果

Linux端效果图如下:

Linux端的(采用UOS+VScode+g++)

image

Linux端代码如下:

1. 服务器端:
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<fstream>
#include <signal.h>
#include <sys/wait.h>
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 1024
using namespace std;
void read_childproc(int sig);
int main() {
	pid_t pid;
	struct sigaction act;
	int strlen;
	//配置信号处理函数
	act.sa_handler=read_childproc;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;
	sigaction(SIGCHLD, &act, 0);
	//创建套接字
	int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//将套接字和IP、端口绑定
	struct sockaddr_in serv_addr;
	memset(&serv_addr, 0, sizeof(serv_addr));
	//每个字节都用0填充
	serv_addr.sin_family = AF_INET;
	//使用IPv4地址
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	//具体的IP地址
	serv_addr.sin_port = htons(1234);
	//端口
	bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	//进入监听状态,等待用户发起请求
	listen(serv_sock, 20);
	//接收客户端请求
	char buffer[BUF_SIZE] = {
		0
	}
	;
	//缓冲区
	for (int i=0;;i++) {
		struct sockaddr_in clnt_addr;
		socklen_t clnt_addr_size = sizeof(clnt_addr);
		int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
		if (clnt_sock == -1)
		cout<<"某一客户端断开"<<endl; else
		cout<<"已连接客户端:"<<i + 1<<endl;
		pid=fork();
		//创建新进程
		if(pid==-1) {
			close(clnt_sock);
			continue;
		}
		if(pid==0) //子进程运行区域 {
			close(serv_sock);
			//在子进程中要关闭服务器套接字
			while((strlen = read(clnt_sock, buffer, sizeof(buffer)))!=0) {
				//接收客户端发来的数据
				write(clnt_sock,buffer,strlen);
				//将数据原样返回
				FILE *fp = fopen("result.txt","a");
				//客户端传过来的数据写入文件
				fputs(buffer,fp);
				//客户端传过来的数据写入文件
				fclose(fp);
				//关闭文件
			}
			close(clnt_sock);
			//关闭套接字
			cout<<"客户端断开"<<endl;
			return 0;
		} else //父进程运行区域 {
			//调用fork函数后,要将无关的套接字文件描述符关闭掉
			close(clnt_sock);
		}
		// memset(buffer, 0, BUF_SIZE); //重置缓冲区
	}
	//关闭套接字
	close(serv_sock);
	return 0;
}
//一旦有子进程结束就调用这个函数
void read_childproc(int sig) {
	int status;
	pid_t id=waitpid(-1, &status, WNOHANG);
	if(WIFEXITED(status)) {
		cout<<"结束进程,id: "<<id<<endl;
		cout<<"子进程发送: "<<WEXITSTATUS(status)<<endl;
	}
}
//g++ 网络编程作业5服务器端.cpp -o test1
2. 客户端:
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
void read_routine(int sock, char *buf);
void write_routine(int sock, char *buf);
using namespace std;
int main() {
	//设置套接字相关属性
	pid_t pid;
	struct sockaddr_in serv_addr;
	struct sockaddr_in sock_addr;
	memset(&sock_addr,0,sizeof(sock_addr));
	sock_addr.sin_family = AF_INET;
	sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	sock_addr.sin_port = htons(1234);
	//发送缓冲区和接收缓冲区
	char bufSend[BUFFER_SIZE];
	int result;
	int num;
	//创建套接字
	int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	connect(sock,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
	pid=fork();
	if(pid==0) //子进程写
	write_routine(sock, bufSend); else //父进程读
	read_routine(sock, bufSend);
}
void read_routine(int sock, char* buf) {
	while(1) {
		int str_len=read(sock, buf, BUFFER_SIZE);
		if(str_len==0)
		return;
		//接受到EOF结束符时返回
		buf[str_len]=0;
		cout<<"message from server: "<<buf<<endl;
	}
}
void write_routine(int sock, char* buf) {
	while(1) {
		fgets(buf, BUFFER_SIZE, stdin);
		if(!strcmp(buf, "q\n") || !strcmp(buf, "Q\n")) {
			shutdown(sock, SHUT_WR);
			return;
		}
		write(sock, buf, strlen(buf));
	}
}
//g++ 网络编程作业5客户端.cpp -o test2
posted @ 2021-04-23 22:55  戈小戈  阅读(460)  评论(0编辑  收藏  举报