網絡編程:實現多進程並發回聲服務器端/客戶端


網絡編程:實現多進程並發回聲服務器端/客戶端

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM