嵌入式操作系统实验——进程间通信Socket(2)

一、实验目的

1、了解采用Socket通信的原理。
2、掌握Socket的创建及使用方法。

二、实验内容

1、创建一个服务器端和若干个客户端。
服务器端可实现包括:接收并区分来自客户端的数据,将用户输入的内容在服务器上输出,并将原内容群发至所有在线客户端(类似qq群聊形式);服务器可主动向在线客户端发送数据;并可以统计在线人数等。
客户端可实现包括:输入文字并且向服务器发送消息,接收来自服务器端的数据;用户控制客户端退出。
2、在服务器端和客户端基于socket实现其通信过程。
3、 服务器与客户端的具体内容可根据实际工作情况自由发挥,充分体现原创精神即可。需要:1)简述程序功能;2)具体的实现源代码及注释;3)实现的过程和结果截图。

三、实验步骤、源程序及结果截图

Client.c文件:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#define SOCK_PORT 666
#define BUFFER_LENGTH 1024
int sock_fd;
struct sockaddr_in s_addr_in;
char data_send[BUFFER_LENGTH];
char data_recv[BUFFER_LENGTH];
void init_client();
static void Data_read();
int main() {
	setvbuf(stdout, NULL, _IONBF, 0);//定义流如何缓冲
	init_client();
	pthread_t thread_id;//声明线程ID
	if (-1 == pthread_create(&thread_id, NULL, (void *)(&Data_read), (void *)(NULL))) {
		perror("客户端创建接收线程失败\n");
		exit(EXIT_FAILURE);
	}//创建接收线程
	char input[64];

	printf("请输入SEND后发送信息;输入CLOSE后关闭聊天\n");
	do {
		gets(input);
		
		if (strcmp(input, "CLOSE") == 0) {
		int ret = shutdown(sock_fd, SHUT_WR);
			assert(ret != -1);
			break;
		}
		else if (strcmp(input, "SEND") == 0) {
			memset(data_send, 0, BUFFER_LENGTH);
			printf("请输入要发送的数据:");
			scanf("%s", data_send);
			int write_res = write(sock_fd, data_send, BUFFER_LENGTH);
			if (write_res == -1) {
				perror("客户端写入失败\n");
				exit(-1);
			}
		}
	}while(1);
	return 0;
}
void init_client() {
	sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET,是 IPv4 网络协议的套接字类型;SOCK_STREAM字节流;
	if (sock_fd == -1) {
		perror("客户端Socket创建失败");
		exit(-1);
	}
	memset(&s_addr_in, 0, sizeof(s_addr_in));
	s_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");//绑定本机;inet_addr一个点分十进制字符串表示的IP转换成一个长整型数
	s_addr_in.sin_family = AF_INET;
	s_addr_in.sin_port = htons(SOCK_PORT);
	int connect_res = connect(sock_fd, (struct sockaddr *) (&s_addr_in), sizeof(s_addr_in));
	if (connect_res == -1) {
		perror("客户端连接失败");
		exit(-1);
	}
	memset(data_send, 0, BUFFER_LENGTH);
	memset(data_recv, 0, BUFFER_LENGTH);
}
static void Data_read() {
	while (true)
	{
		int read_res = read(sock_fd, data_recv, BUFFER_LENGTH);
		assert(read_res != -1);
		if (read_res == 0)
		{
			printf("服务器端已关闭\n");
			exit(EXIT_SUCCESS);
		}
		printf("Server>: %s\n", data_recv);
		memset(data_recv, 0, BUFFER_LENGTH);
	}
}

server.c文件:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#define SERVER_PORT 666
#define MAX_CONN_LIMIT 8 //MAX connection limit
#define BUFFER_LENGTH 1024
// Server
struct sockaddr_in server_addr;
int server_sock_fd;
// Server Pool
int socket_pool[64];//链接池中最大人数限制在64人
char data_send[BUFFER_LENGTH];
char data_recv[BUFFER_LENGTH];
void init_server();
void init_pool();
void close_server();
void show_help();
int get_num();
int next_index();
void send_data(int sock_fd);
bool in_pool(int sock_fd);
static void accept_thread();
static void Data_handle(void* sock_fd);
int main() {
	setvbuf(stdout, NULL, _IONBF, 0);
	// 初始化
	init_server();
	init_pool();
	pthread_t thread_id;
	if (-1 == pthread_create(&thread_id, NULL, (void*)(&accept_thread), (void*)(NULL))) {
		perror("服务器端创建接收thread失败\n");
		exit(EXIT_FAILURE);
	}
	char input[64];
	while (1) {
		scanf("%s", input);
		if (strcmp(input, "CLOSE") == 0) {
			close_server();
			break;
		}
		else if (strcmp(input, "HELP") == 0) {
			show_help();
		}
		else if (strcmp(input, "ALL") == 0) {
			memset(data_send, 0, BUFFER_LENGTH);
			printf("请输入要发送的消息:");
			scanf("%s", data_send);
			for (int i = 0; i < 64; i++) {
				if (socket_pool[i] != -1) {
					send_data(socket_pool[i]);
				}
			}
		}
		else if (strcmp(input, "NUM") == 0) {
			printf("当前在线人数为%d\n", get_num());
		}
		else if (strcmp(input, "ONE") == 0) {
			int tmp_fd;
			printf("请输入要发送的ID:");
			scanf("%d", &tmp_fd);
			if (!in_pool(tmp_fd)) {
				printf("该ID不存在\n");
				continue;
			}
			memset(data_send, 0, BUFFER_LENGTH);
			printf("请输入要发送的消息:");
			scanf("%s", data_send);
			send_data(tmp_fd);
		}
		else {
			printf("输入命令错误\n");
		}
	}
	return 0;
}
// 显示帮助
void show_help() {
	printf("NUM: 显示当前在线人数\n");
	printf("ALL: 向全体发消息\n");
	printf("ONE: 向部分发消息\n");
	printf("HELP: 显示帮助信息\n");
	printf("CLOSE: 关闭服务器端\n");
}
// 初始化服务
void init_server() {
	// 初始化
	memset(&server_addr, 0, sizeof(struct sockaddr_in));
	// 使用 IPv4 进行通信
	server_addr.sin_family = AF_INET;
	// 设置端口
	server_addr.sin_port = htons(SERVER_PORT);
	// 指向IP
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	// 初始化空字节
	bzero(&(server_addr.sin_zero), 8);
	// 创建socket
	server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == server_sock_fd) {
		perror("服务器端Socket创建失败");
		exit(-1);
	}
	//  close socket后想继续重用该socket
	int on = 1;
	setsockopt(server_sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	// 绑定
	int bind_result = bind(server_sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if (-1 == bind_result) {
		perror("服务器端Socket绑定失败");
		exit(-1);
	}
	// 置socket为listen模式
	int listen_result = listen(server_sock_fd, MAX_CONN_LIMIT);
	if (-1 == listen_result) {
		perror("服务器端Socket设置listen模式失败");
		exit(-1);
	}
}
// 初始化链接池
void init_pool() {
	memset(socket_pool, -1, sizeof(int) * 64);
}
// 关闭
void close_server() {
	int shutdown_result = shutdown(server_sock_fd, SHUT_WR);
	if (-1 == shutdown_result) {
		printf("服务器端Socket关闭异常\n");
		exit(-1);
	}
	printf("服务器端Socket正确关闭\n");
}
// 接收线程
void accept_thread() {
	int client_length;
	struct sockaddr_in s_addr_client;
	int client_sock_fd;
	while (1) {
		pthread_t thread_id;
		client_length = sizeof(s_addr_client);
		// Accept
		client_sock_fd = accept(server_sock_fd, (struct sockaddr_*)(&s_addr_client), (socklen_t*)(&client_length));
		if (client_sock_fd == -1) {
			perror("服务器端Accept错误");
			continue;
		}
		printf("服务器端接收到一个新请求\n");
		if (pthread_create(&thread_id, NULL, (void*)(&Data_handle), (void*)(&client_sock_fd)) == -1) {
			perror("服务器端建立thread失败\n");
			break;
		}
		printf("该客户端ID为%d\n", client_sock_fd);
		// 存储
		int idx = next_index();
		if (idx == -1) {
			perror("服务器链接已满");
			exit(EXIT_FAILURE);
		}
		else {
			socket_pool[idx] = client_sock_fd;
		}
	}
}
// next INDEX
int next_index() {
	for (int i = 0; i < 64; i++) {
		if (socket_pool[i] == -1) {
			return i;
		}
	}
	return -1;
}
// 得到当前在线人数
int get_num() {
	int num = 0;
	for (int i = 0; i < 64; ++i) {
		if (socket_pool[i] != -1) {
			num += 1;
		}
	}
	return num;
}
// 是否在连接池中
bool in_pool(int sock_fd) {
	for (int i = 0; i < 64; i++) {
		if (socket_pool[i] == sock_fd) {
			return true;
		}
	}
	return false;
}
// 数据处理
static void Data_handle(void* sock_fd) {
	int fd = *((int*)sock_fd);
	int i_recvBytes;
	int i;
	while (1) {
		//Reset data.
		memset(data_recv, 0, BUFFER_LENGTH);
		i_recvBytes = read(fd, data_recv, BUFFER_LENGTH);
		printf("i_recv = %d\n", i_recvBytes);
		if (i_recvBytes == 0) {
			printf("该客户端%d关闭\n", fd);
			break;
		}
		else if (i_recvBytes == -1) {
			perror("服务器端读取错误");
			break;
		}
		else {
			printf("客户端[%d]: %s\n", fd, data_recv);
			memset(data_send, 0, BUFFER_LENGTH);
			for(i = 0;i< BUFFER_LENGTH;i++)
				data_send[i] = data_recv[i];
			for (int i = 0; i < 64; i++) {
				if (socket_pool[i] != -1) {
					send_data(socket_pool[i]);
				}
			}


		}
	}
	//Clear
	printf("客户端[%d]结束\n", fd);
	close(fd);//close a file descriptor.
	pthread_exit(NULL);//terminate calling thread!
}
// 发送消息
void send_data(int sock_fd) {
	int res = write(sock_fd, data_send, BUFFER_LENGTH);
	if (res == -1) {
		perror("发送失败");
		exit(EXIT_FAILURE);
	}
}

功能实现:
服务器端可以连接多个客户端,客户端可以向服务器端发送消息,然后服务端将消息转发给全部的客户端。服务器端也可以向某一指定的客户端发消息,也可以向全部的连接的客户端发送消息,同时可以统计在线人数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值