本人大二电气在读,由于提前修了计算机网络及其衍生实验课之后,对即时通讯软件开发产生兴趣,于是自学开发了一个基于Socket的多线程TCP即时通讯软件
功能:可以实现单对单,多对单,多对多的即时通讯
特性:通信性能强,速度快
创新处:
提高性能:多线程允许同时处理多个TCP连接,提高了程序处理大量并发连接的能力,从而提升了整体的通信性能。
响应速度快:在多线程模型中,可以快速响应用户的请求,因为不同线程可以并行处理不同的任务,减少了等待时间。
使用处:小范围使用,如小型公司,实验室等等,在节约资源的同时,满足加密即时通讯需求
第一章 作品概述
本作品是一款基于Socket的多线程TCP即时通讯软件, 软件功能丰富,支持单对单、多对单以及多对多的通讯模式,能够满足不同用户间多样化的沟通需求。无论是个人之间的私密对话,还是小组内的讨论,或是多人在线会议,该软件都能提供稳定且高效的服务。
在性能方面,软件具有显著的优势。其多线程特性允许同时处理多个TCP连接,显著提升了程序处理大量并发连接的能力。这一特性不仅增强了通信性能,也使得软件能够快速响应用户请求,不同线程并行处理任务,大幅度减少了用户的等待时间。
创新之处在于,该软件不仅在性能上进行了优化,还特别注重响应速度,使得用户体验更加流畅。这对于需要快速、实时通讯的用户来说尤为重要。
此外,该软件还适用于小范围使用,如小型公司、实验室等场合。它能够在节约资源的同时,满足用户对加密即时通讯的需求,为用户提供了一个既安全又高效的通讯平台。
总体来说,这款软件为需要即时通讯服务的小型组织提供了一个实用的解决方案。
第二章 作品设计与实现
开发环境:
系统:Windows 11
语言:C语言
开发工具:Vscode
浅谈Socket:
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。
客户端开发:
思路实现:
socket() 创建一个 Socket
connect() 与服务器连接
进行会话
close() ,关闭 Socket
代码实现:
/*
Description:Socket 服务器端编程主要步骤如下:
1、socket() 创建一个 Socket
2、bind()
3、listen() 监听
4、accept() 接收连接的请求
5、进行会话
6、close() 关闭 Socket
Version: Dev-C++ 5.11
Date: 2020/05/01
*/
#include <stdio.h> //Needed for printf()
#include <string.h> //Needed for memcpy() and strcpy()
#include <winsock2.h>
#include <windows.h> //Needed for all Winsock stuff
#include <ws2tcpip.h> //Needed forsocklen_t
#include <stdlib.h> //解决程序结果一闪而过
#include "dirent.h" //查询文件名需要
//-----------------------------------------------------
#define PORT_NUM 5000 //服务器端口号
#define MAX_LISTEN 3 //最大监听数
#define FILE_NAME_MAX_SIZE 512 //文件名最大长度
#define BUFFER_SIZE 1024*4 //缓冲区大小
#define FilePath "./" //文件目录,提供目录中所有文件名给client
unsigned int server_s; //Server socket 标示符
struct sockaddr_in server_addr; //Server 地址
unsigned int client_s; //Client socket标示符
struct sockaddr_in client_addr; //Client Internet address
struct in_addr client_ip_addr; //Client IP address
int addr_len; //Internet address length
char out_buf[BUFFER_SIZE]; // 1024*4-byte 输出缓冲区
char in_buf[BUFFER_SIZE]; // 1024*4-byte 接收缓冲区
/*初始化,建立Socket */
void SocketInit(){
// 建立socket
if((server_s = socket(AF_INET, SOCK_STREAM, 0))<0){
perror("SocketError");
exit(1);
}else{
printf("Server create socket successfully!\n");
}
// 配置socket
server_addr.sin_family = AF_INET; // Address family to use
server_addr.sin_port = htons(PORT_NUM); // 使用端口号
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //监听任一IP地址
//bind函数将socket与本地协议端口绑定
bind(server_s,(struct sockaddr *)&server_addr, sizeof(server_addr));
}
/*开始监听,等待客户端连接 */
void Listen (){
// 监听连接
printf("Start listening...\n");
listen(server_s, MAX_LISTEN);
}
/*接受连接,显示IP和端口*/
void Accept(){
// 接受连接
addr_len = sizeof(client_addr);
if((client_s = accept (server_s, (struct sockaddr *)&client_addr, &addr_len))<0){
perror("AcceptError");
exit(1);
}else{
printf("Accept successfully!\n");
}
memcpy(&client_ip_addr, &client_addr.sin_addr.s_addr, 4);
//输出一条接收完成的消息
//inet_ntoa将一个十进制网络字节序转换为点分十进制IP格式的字符串
printf("Accept completed!!! IP address of client = %s port = %d \n", inet_ntoa(client_ip_addr), ntohs(client_addr.sin_port));
}
/*向客户端发送数据*/
void SendToClient(){
memset(in_buf,'\0',sizeof(in_buf)); //将in_buf清空,用来接收数据
socklen_t length = sizeof(client_addr);
length = recv(client_s, in_buf, sizeof(in_buf), 0); //接收client发送的文件名
if (length < 0)
{
printf("Server recieve data failed!\n");
exit(1);
}
char file_name[FILE_NAME_MAX_SIZE + 1]; //存放文件名
memset(file_name,'\0',sizeof(file_name)); //将file_name清空,用来存放文件名
//将接收到的文件名放入file_name
strncpy(file_name, in_buf,strlen(in_buf) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(in_buf));
//文件操作
FILE *fp = fopen(file_name, "r");
if (fp == NULL)
{
printf("File:\t%s not found!\n", file_name);
}
else
{
memset(out_buf,'\0',sizeof(out_buf)); //将out_buf清空,用来存放要发送的数据
int file_block_length = 0; //文件长度
//将文件数据存入到out_buf,准备发送
while( (file_block_length = fread(out_buf, sizeof(char), BUFFER_SIZE, fp)) > 0)
{
printf("File length = %d\n", file_block_length);
// 发送buffer中的字符串到client_s,实际上就是发送给客户端
if (send(client_s, out_buf, (strlen(out_buf) + 1), 0) < 0)
{
printf("Send file:\t%s failed!\n", file_name);
break;
}
memset(out_buf,'\0',sizeof(out_buf)); //将out_buf清空,以便下次使用
}
fclose(fp);
printf("File:\t%s transfer finished!\n", file_name);
}
}
/*接收从客户端发送来的数据*/
void ReceiveFromClient(){
char file_name[FILE_NAME_MAX_SIZE + 1]; //存放文件名
memset(file_name,'\0',sizeof(file_name)); //将file_name清空,用来存放文件名
memset(in_buf,'\0',sizeof(in_buf)); //将in_buf清空,用来接收收到的文件名
recv(client_s, in_buf, sizeof(in_buf), 0); //接收client发送过来的文件名
//将接收到的文件名放入file_name
strncpy(file_name, in_buf, strlen(in_buf) > BUFFER_SIZE ? BUFFER_SIZE : strlen(in_buf));
//文件操作
FILE *fp = fopen(file_name, "w");
if (fp == NULL)
{
printf("File:\t%s Can not open to write!\n", file_name);
exit(1);
}
// 从服务器端接收数据到in_buf中
memset(in_buf,'\0',sizeof(in_buf)); //将in_buf清空,用来接受数据
int length = 0;
//接收client发送来的数据
while(length = recv(client_s, in_buf, sizeof(in_buf), 0))
{
if (length < 0)
{
printf("Recieve data from server failed!\n");
break;
}
//将接收的数据写入文件
int write_length = fwrite(in_buf, sizeof(char), length, fp);
if (write_length < length)
{
printf("File:\t%s write failed!\n", file_name);
break;
}
memset(in_buf,'\0',sizeof(in_buf)); //将in_buf清空,以便下次使用
}
fclose(fp); //关闭文件
printf("Recieve file:\t %s from client finished!\n", file_name);
}
/*
发送所有文件名给client以便列表出server所有文件