实验要求:
利用Socket实现双机通信
实验目的:
利用Socket编程,采用其中的TCP面向连接方式,实现计算机数据的交换
具体要求:
- 操作系统:可在Linux或Windows操作系统下实现
- 编程语言:C或C++,若使用Java、python等更高级的语言或脚本语言则须实现下面的“扩展功能”
- 界面要求:图形界面或者命令行界面均可
- 连接方式:局域网内通信,有线或无线网络均可
- 实现功能:
- 基本功能(必做):计算机A、B(须为不同的物理机)实现文本通信,即B收到A所发送的文本并显示。
- 扩展功能(加分选做):双向通信(同一程序下,A、B既可以发送数据又可以接受数据);文本传输(B完整收到A发送的文件并保存);实现音视频传输(类似于QQ电话);其他网络拓展功能
- 验收方式等不再赘述
实现
基于C++和MacOS的实现,unix上可以正常运行,linux好像有库不支持。有关Socket的基础知识见博客网络socket编程指南,下面代码中要实现的内容有:
- 基本文本双向通信,多对多双向通信,实现私聊和群聊功能
- 文件双向传输,多对多双向传送,实现私传和群传功能
题目要求使用TCP方式,因此应该使用流式套接字。代码使用127.0.0.1测试成功,一主机对一虚拟机也测试成功了,尚未进行多台电脑之间的测试(疫情在家,买不起多台电脑)。
由于没有设计GUI,我们需要对客户端的输入格式进行规范化,如下所示:
首先输入聊天对象:
1 ID
表示私聊客户端ID,2
表示进入群聊,向包括服务端的所有人发送信息。
选择聊天对象后:
1 文本
表示发送文本信息,2 PATH
表示将PATH路径上的文件发送过去,3
表示开始视频通话(此功能尚未实现)。之后的聊天不需要输入聊天对象,默认是与此人聊天。
键入\exit
表示退出与此人的聊天,下次需要重新输入聊天对象。
键入\quit
表示退出与服务器的连接。
服务端可以对群体进行文件传输和文本发送,和上面相同,1 文本
表示发送文本信息,2 PATH
表示将PATH路径上的文件发送过去,3
表示开始视频通话(此功能尚未实现)。
暂时没有实现任意多个人之间进行聊天。代码有些杂乱,并且或多或少可能有一些bug,但是可以正常运行。我尽量抽时间整理,见谅。
运行界面
服务端
客户端1
客户端2
完整代码
服务端
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fstream>
const int BACKLOG=2; //完成三次握手但没有accept的队列的长度
const int CONCURRENT_MAX=3; //应用层同时可以处理的连接
const int BUFFER_SIZE=1024;//将被加入缓存器的最大字节数
char input_msg[BUFFER_SIZE];
char recv_msg[BUFFER_SIZE];
class Socket_server{
public:
Socket_server(){
server_sock_fd=0;
tv.tv_sec = 20;//秒
tv.tv_usec = 0;//微秒
max_fd=-1;
memset(client_fds,0,sizeof(client_fds));
memset(client_to,-1,sizeof(client_to));
filename.clear(); path.clear();
};
~Socket_server(){
};
bool init(const int port);
bool start();
bool sendmessage();//服务器发出信息
bool sendIP(int index);//发送号码表
void recv_and_forward();//接收信息,同时起到中转站的作用
void recv_word(int index,int byte_num);
void recv_file(int index,int byte_num);
void recv_video(int index);
void accept_new();
private:
int server_sock_fd;//服务端套接字
//fd_set
fd_set server_fd_set;//用来存放所有的待检查的文件描述符
int max_fd;
timeval tv;//struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
int client_fds[CONCURRENT_MAX+1];//客户端套接字
struct sockaddr_in client_addr[CONCURRENT_MAX+1];//客户端对应的地址信息
int client_to[CONCURRENT_MAX+1];//客户端想要发送的对象
std::string filename;
std::string path;
};
bool Socket_server::init(const int port){
struct sockaddr_in server_addr;//sockaddr_in: Socket address, internet style.表示网络地址的结构体(internet和socket通用)
server_addr.sin_len = sizeof(struct sockaddr_in);
server_addr.sin_family = AF_INET;//socket只能用这个
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// server_addr.sin_port = htons(0);//16位整数 主机字节序转网络字节序,此处为设置端口号(本机是小端存储,网络字节是大端)
// server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//设置本机IP地址
memset(&(server_addr.sin_zero),0,8);
//创建socket
server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock_fd == -1){//未能成功打开网络通讯端口
perror("socket error");//输出字符串+errno对应的错误
return false;
}
int on = 1;
if(setsockopt(server_sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))==-1){//防止出现bind error的地址已被占用
perror("setsockopt");
return false;
}
//绑定socket
int bind_result = bind(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (bind_result == -1){
perror("bind error");
return false;
}
//listen
if (listen(server_sock_fd, BACKLOG) == -1){
perror("listen error");
return false;
}
printf("欢迎使用SOCKET通讯系统,输入quit并确认可退出\n");
return true;
}
bool Socket_server::start()
{
FD_ZERO(&server_fd_set);
//标准输入
FD_SET(STDIN_FILENO, &server_fd_set);//用于在文件描述符集合中增加一个新的文件描述符。STDIN_FILENO是标准输入文件描述符
if (max_fd < STDIN_FILENO)//STDIN_FILENO = 0
max_fd = STDIN_FILENO;
//服务器端socket
FD_SET(server_sock_fd, &server_fd_set);//把要检测的套接字server_sock_fd加入到集合中
if (max_fd < server_sock_fd){
max_fd = server_sock_fd;
}
//客户端连接
for (int i = 1; i <= CONCURRENT_MAX; i++){
if (client_fds[i]!=0){
FD_SET(client_fds[i], &server_fd_set);
if (max_fd < client_fds[i])//找到文件描述符中的最大值并存储
max_fd = client_fds[i];
}
}
int ret = select(max_fd+1, &server_fd_set, NULL, NULL, &tv);//检测server_fd_set中的套接字中是否有可读信息
if (ret < 0){
perror("select 出错");
return false;
}
else if(ret == 0){
printf("timeout...\n");
return false;
}
return true;
}
bool Socket_server::sendmessage()
{
//ret为未状态发生变化的文件描述符的个数
if (FD_ISSET(STDIN_FILENO, &server_fd_set)){//检测是否可写,可发送语句
//标准输入
memset(input_msg,0,sizeof(input_msg));
fgets(input_msg, BUFFER_SIZE, stdin);
char *find = strchr(input_msg, '\n');//查找换行符
if(find) *find = '\0';//如果find不为空指针,就把一个空字符放在这里
if (strcmp(input_msg, "\\quit") == 0){
shutdown(server_sock_fd,SHUT_RDWR);
return false;
}
if(input_msg[0]=='1' && input_msg[1]==' '){
for (int i=1; i<=CONCURRENT_MAX; i++){//向每个连接的客户端发送信息
if (client_fds[i]!=0){
std::string s; s.clear(); s="0";//表明这是谁发过来的
if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
perror("发送消息出错!");
if(send(client_fds[i], input_msg, BUFFER_SIZE, 0)==-1)
perror("发送消息出错!");
}
}
}
else if(input_msg[0]=='2' && input_msg[1]==' '){
for (int i=1; i<=CONCURRENT_MAX; i++){
if(client_fds[i]!=0){
std::string s; s.clear();
s="0";//表明这是谁发过来的
if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
perror("转发消息出错!");
}
}
for (int i=1; i<=CONCURRENT_MAX; i++){
if(client_fds[i]!=0){
if(send(client_fds[i],input_msg,BUFFER_SIZE,0)<0)
perror("发送消息出错!");
}
}
path.clear();
for (int i=2; input_msg[i]!='\n' && input_msg[i]!='\0'; i++){
path+=input_msg[i];
}
FILE *fp=fopen(path.c_str(),"rb+");
char chtemp[BUFFER_SIZE+1];
memset(chtemp,'\0',sizeof(chtemp));
int cnt=0;
if(fp==nullptr){
printf("文件无法打开\n");
}
else {
while((cnt=fread(chtemp,sizeof(char),BUFFER_SIZE,fp))>0){
for (int i=1; i<=CONCURRENT_MAX; i++){
if(client_fds[i]!=0){
if(send(client_fds[i],chtemp,cnt,0)<0){
perror("发送消息出错!");
}
}
}
memset(chtemp,'\0',sizeof(chtemp));
}
fclose(fp);
printf("Transfer Successful!\n");
}
path.clear(); filename.clear();
}
else if(input_msg[0]=='3' && input_msg[1]==' '){
printf("敬请期待!\n");
}
}
return true;
}
bool Socket_server::sendIP(int index)
{
std::string s1="0";
send(client_fds[index],s1.c_str(),BUFFER_SIZE,0);
s1.clear();
s1="1 您现在是"+std::to_string(index)+"号客户机, 当前聊天室内成员如下:\n";
send(client_fds[index],s1.c_str(),BUFFER_SIZE,0);
for (int i=1; i<=CONCURRENT_MAX; i++){
if(client_fds[i]!=0){
s1.clear();
s1="0";
send(client_fds[index],s1.c_str(),BUFFER_SIZE,0);
s1.clear();
s1="1 "+std::to_string(i)+"号客户机,IP地址为:"+inet_ntoa(client_addr[i].sin_addr)+"\n";
if(send(client_fds[index],s1.c_str(),BUFFER_SIZE,0)==-1) return false;
}
if(client_fds[i]!=0 && i!=index){
s1.clear();
s1="0";
send(client_fds[i],s1.c_str(),BUFFER_SIZE,0);
s1.clear();
s1="1 新加入了"+std::to_string(index)+"号客户机,IP地址为:"+inet_ntoa(client_addr[index].sin_addr)+"\n";
if(send(client_fds[i],s1.c_str(),BUFFER_SIZE,0)==-1) return false;
}
}
return true;
}
void Socket_server::recv_word(int index,int byte_num)
{
if(client_to[index]==0 || client_to[index]==-2){//发送给服务器的或者群发
printf("客户端(%d):",index);
for (int j=2; j<byte_num; j++){
if(recv_msg[j]=='\0') break;
printf("%c",recv_msg[j]);
}
printf("\n");
}
if(client_to[index]==-2){//群发其余客户端
for (int i=1; i<=CONCURRENT_MAX; i++){
if(client_fds[i]!=0 && i!=index){
//先给id发送这是谁发过来的
std::string s; s.clear();
s=std::to_string(index);//表明这是谁发过来的
if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
perror("转发消息出错!");
if (send(client_fds[i], recv_msg, BUFFER_SIZE, 0) == -1){
perror("转发消息出错!");
}
}
}
}
else if(client_to[index]!=0){//发送给个人的
//先给id发送这是谁发过来的
std::string s; s.clear();
s=std::to_string(index);//表明这是谁发过来的
if(send(client_fds[client_to[index]],s.c_str(),BUFFER_SIZE,0)==-1)
perror("转发消息出错!");
if (send(client_fds[client_to[index]], recv_msg, BUFFER_SIZE, 0) == -1){
perror("转发消息出错!");
}
}
}
void Socket_server::recv_file(int index,int byte_num)
{
std::string tempfilename; tempfilename.clear();
if(client_to[index]==0 || client_to[index]==-2){//发送给服务器的或者群发
int pos=0;
for (int j=byte_num-1; j>0; j--){
if(recv_msg[j]=='/' || recv_msg[j]=='\\'){
pos=j; break;
}
}
for (int j=pos+1; j<byte_num; j++){
if(recv_msg[j]=='\0') break;
filename+=recv_msg[j];
}
//取得文件名称,准备建立新的文件
printf("客户端(%d)向你传送了一个文件,请输入您想要存放的绝对路径,例如:/Users/longzhengtian/Desktop/\n注意,最后要有一个'/'符号,如果没有,系统将自动填充,请输入:",index);
std::cin>>path; getchar();
if(path[path.size()-1]!='/'){//如果没有'/'的补救措施
path+="/";
}
//filename="new_"+filename;
path+=filename;
FILE *fp=fopen(path.c_str(),"wb+");
if(fp==nullptr)
printf("接收文件出错!\n");
else {
memset(recv_msg,0,sizeof(recv_msg));
int cnt=0;
while((cnt=recv(client_fds[index],recv_msg,BUFFER_SIZE,0)) && cnt>0){
if(fwrite(recv_msg,sizeof(char),cnt,fp)<cnt){
printf("接收文件出错!\n");
break;
}
memset(recv_msg,0,sizeof(recv_msg));
if(cnt!=BUFFER_SIZE) break;
}
printf("Receive successful!\n");
fclose(fp);
}
if(client_to[index]==0) path.clear(); filename.clear();
}
if(client_to[index]==-2){//群发
for (int i=1; i<=CONCURRENT_MAX; i++){
if(client_fds[i]!=0 && i!=index){
std::string s; s.clear();
s=std::to_string(index);//表明这是谁发过来的
if(send(client_fds[i],s.c_str(),BUFFER_SIZE,0)==-1)
perror("转发消息出错!");
}
}
filename="2 "+path;
for (int i=1; i<=CONCURRENT_MAX; i++){
if(i!=index && client_fds[i]!=0){
if(send(client_fds[i],filename.c_str(),BUFFER_SIZE,0)<0)
perror("发送消息出错!");
}
}
FILE *fp=fopen(path.c_str(),"rb+");
char chtemp[BUFFER_SIZE+1];
memset(chtemp,'\0',sizeof(chtemp));
int cnt=0;
if(fp==nullptr){
printf("文件无法打开\n");
}
else {
while((cnt=fread(chtemp,sizeof(char),BUFFER_SIZE,fp))>0){
for (int i=1; i<=CONCURRENT_MAX; i++){
if(i!=index && client_fds[i]!=0){
if(send(client_fds[i],chtemp,cnt,0)<0){
perror("发送消息出错!");
}
}
}
memset(chtemp,'\0',sizeof(chtemp));
}
fclose(fp);
printf("Transfer Successful!\n");
}
path.clear(); filename.clear();
}
else if(client_to[index]!=0){//发送给个人的
std::string s; s.clear();
s=std::to_string(index);//表明这是谁发过来的
if(send(client_fds[client_to[index]],s.c_str(),BUFFER_SIZE,0)==-1)
perror("转发文件出错!");
if(send(client_fds[client_to[index]],recv_msg,BUFFER_SIZE,0)==-1)
perror("转发文件出错!");
memset(recv_msg,0,sizeof(recv_msg));
int cnt=0;
while((cnt=recv(client_fds[index],recv_msg,BUFFER_SIZE,0)) && cnt>0){
if(send(client_fds[client_to[index]],recv_msg,cnt,0)==-1)
perror("转发文件出错!");
memset(recv_msg,0,sizeof(recv_msg));
if(cnt!=BUFFER_SIZE) break;
}
printf("A file from Client%d to Client%d was successfully forwarded through this server\n",index,client_to[index]);
}
}
void Socket_server::recv_video(int index)
{
printf("敬请期待!\n");
}
void Socket_server::recv_and_forward()
{
for (int i = 1; i <= CONCURRENT_MAX; i++){
if (client_fds[i]!=0){
if (FD_ISSET(client_fds[i], &server_fd_set)){
//处理某个客户端过来的消息
memset(recv_msg,0,sizeof(recv_msg));
int byte_num = recv(client_fds[i],recv_msg,BUFFER_SIZE,0);
if (byte_num > 0){
if (byte_num > BUFFER_SIZE) byte_num = BUFFER_SIZE;
recv_msg[byte_num] = '\0';
if(strcmp(recv_msg,"\\exit")==0){//客户端要求解除连接
client_to[i]=-1;
}
else if(client_to[i]==-1){//这条信息是对面发来指定发送对象的
if(recv_msg[0]=='-') client_to[i]=-2;
else client_to[i]=recv_msg[0]-'0';
}
else if(recv_msg[0]=='1'){//文字
recv_word(i,byte_num);
}
else if(recv_msg[0]=='2'){//文件
recv_file(i,byte_num);
}
else if(recv_msg[0]=='3'){//视频
recv_video(i);
}
}
else if(byte_num < 0)
printf("从客户端(%d)接受消息出错.\n",i);
else{
FD_CLR(client_fds[i], &server_fd_set);//用于在文件描述符集合中删除一个文件描述符。
client_fds[i] = 0;
printf("客户端(%d)退出了\n",i);
std::string s1;
for (int j=1; j<=CONCURRENT_MAX; j++){
if(client_fds[j]!=0 && i!=j){
s1.clear();
s1=std::to_string(i)+"号客户机退出了!\n";
send(client_fds[j],s1.c_str(),BUFFER_SIZE,0);
}
}
}
}
}
}
}
void Socket_server::accept_new()
{
if (FD_ISSET(server_sock_fd, &server_fd_set)){//检测server_sock_fd是否真的变成了可读,可接受语句
//有新的连接请求
struct sockaddr_in client_address;
socklen_t address_len=sizeof(struct sockaddr_in);
int client_socket_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &address_len);//对应客户端对connect
if (client_socket_fd > 0){
int index = -1;
for (int i = 1; i <= CONCURRENT_MAX; i++){
if (client_fds[i] == 0){
index = i;
client_fds[i] = client_socket_fd;
break;
}
}
if (index >= 0){
printf("新客户端(%d)加入成功 %s:%d \n",index,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
//inet_ntoa:接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串
client_addr[index]=client_address;
sendIP(index);
}
else{
memset(input_msg,0,sizeof(input_msg));
strcpy(input_msg, "服务器加入的客户端数达到最大值!\n");
send(client_socket_fd, input_msg, BUFFER_SIZE, 0);
printf("客户端连接数达到最大值,新客户端加入失败 %s:%d \n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
}
}
}
}
int main (){
//本地地址
Socket_server tcpserver;
tcpserver.init(11111);//初始化,创建并绑定套接字,开始监听
while (true){
if(!tcpserver.start()) {//建立连接失败
continue;
}
else{
tcpserver.accept_new();//是否有新的加入
if(!tcpserver.sendmessage()) exit(0);//发送信息,包含退出操作
tcpserver.recv_and_forward();//接受信息。
}
}
return 0;
}
客户端
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fstream>
const int BUFFER_SIZE=1024;
char recv_msg[BUFFER_SIZE];
char input_msg[BUFFER_SIZE];
class Socket_client
{
public:
Socket_client(){
server_sock_fd=0;
tv.tv_sec = 20;
tv.tv_usec = 0;
toID=-1;
fromID=0;
}
~Socket_client(){
}
bool start();
bool init();
bool connect_s();
bool sendmessage();
void receive();
void recvmessage();
private:
int server_sock_fd;
struct sockaddr_in server_addr;
fd_set client_fd_set;
struct timeval tv;
int toID;//-1是初始化,-2是群发,0以上是私聊
int fromID;
std::string filename;
std::string path;
};
bool Socket_client::init()
{
server_addr.sin_len = sizeof(struct sockaddr_in);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(11111);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(&(server_addr.sin_zero),0,8);
server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock_fd == -1){
perror("socket error");
return false;
}
return true;
}
bool Socket_client::connect_s()
{
if(connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in))==0) {
printf("服务器加入成功!\n");
return true;
}
else return false;
}
bool Socket_client::start()
{
FD_ZERO(&client_fd_set);
FD_SET(STDIN_FILENO, &client_fd_set);
FD_SET(server_sock_fd, &client_fd_set);
int ret = select(server_sock_fd + 1, &client_fd_set, NULL, NULL, &tv);
if (ret < 0 ){
printf("select 出错!\n");
return false;
}
else if(ret ==0){
printf("timeout...\n");
return false;
}
return true;
}
bool Socket_client::sendmessage()
{
if (FD_ISSET(STDIN_FILENO, &client_fd_set)){
if(toID==-1){//没有制定发送对象,0号表示服务器
int a=-1,b=-1;
scanf("%d",&a);
if(a==1) {
scanf("%d",&b); toID=b;
}
else toID=-2;
std::string s=std::to_string(toID);
if(send(server_sock_fd,s.c_str(),BUFFER_SIZE,0)==-1){
perror("制定发送对象出错!");
}
getchar();
}
else {
memset(input_msg,0,sizeof(input_msg));
fgets(input_msg, BUFFER_SIZE, stdin);
char *find = strchr(input_msg, '\n');//查找换行符
if(find)//如果find不为空指针
*find = '\0';//就把一个空字符放在这里
if (strcmp(input_msg, "\\quit") == 0){
shutdown(server_sock_fd,SHUT_RDWR);
return false;
}
if (strcmp(input_msg, "\\exit") == 0){
toID=-1;
send(server_sock_fd,input_msg,5,0);
return true;
}
if(input_msg[0]=='1'){//文字传送
if (send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1){
perror("发送消息出错!");
}
}
else if(input_msg[0]=='2'){//文件传输
if (send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1){
perror("文件传输出错!");
}
std::string _path;
for (int i=2; input_msg[i]!='\n' && input_msg[i]!='\0'; i++){
_path+=input_msg[i];
}
FILE *fp=fopen(_path.c_str(),"rb+");
char chtemp[BUFFER_SIZE+1];
memset(chtemp,'\0',sizeof(chtemp));
int cnt=0;
if(fp==nullptr){
printf("文件无法打开\n");
}
else {
while((cnt=fread(chtemp,sizeof(char),BUFFER_SIZE,fp))>0){
if(send(server_sock_fd,chtemp,cnt,0)<0){
perror("发送消息出错!");
}
memset(chtemp,'\0',sizeof(chtemp));
}
fclose(fp);
printf("Transfer Successful!\n");
}
}
else if(input_msg[0]=='3'){//视频聊天
printf("您选择了视频聊天,功能尚未完成\n");
}
else {
printf("传输形式不合法,请重新输入。\n");
}
}
}
return true;
}
void Socket_client::recvmessage()
{
fromID=0;
//先接收到的是发送方ID;
for (int i=0; recv_msg[i]!='\0'; i++){
fromID*=10;
fromID+=recv_msg[i]-'0';
}
memset(recv_msg,0,sizeof(recv_msg));
memset(recv_msg,0,sizeof(recv_msg));
int byte_num=recv(server_sock_fd,recv_msg,BUFFER_SIZE,0);
if(byte_num>0){
if (byte_num > BUFFER_SIZE) byte_num = BUFFER_SIZE;
recv_msg[byte_num] = '\0';
if(recv_msg[0]=='1' && recv_msg[1]==' '){//文字传输
if(fromID==0) printf("服务器:");
else printf("客户端(%d):",fromID);
for (int j=2; j<byte_num; j++){
if(recv_msg[j]=='\0' || recv_msg[j]=='\n') break;
printf("%c",recv_msg[j]);
}
printf("\n");
}
else if(recv_msg[0]=='2' && recv_msg[1]==' '){
int pos=0;
for (int j=byte_num-1; j>0; j--){
if(recv_msg[j]=='/' || recv_msg[j]=='\\'){
pos=j; break;
}
}
for (int j=pos+1; j<byte_num; j++){
if(recv_msg[j]=='\0') break;
filename+=recv_msg[j];
}
//取得文件名称,准备建立新的文件
if(fromID==0) printf("服务器");
else printf("客户端(%d)",fromID);
printf("向你传送了一个文件,请输入您想要存放的绝对路径,例如:/Users/longzhengtian/Desktop/\n注意,最后要有一个'/'符号,如果没有,系统将自动填充,请输入:");
std::cin>>path; getchar();
if(path[path.size()-1]!='/'){//如果没有'/'的补救措施
path+="/";
}
//filename="new_"+filename;
path+=filename;
FILE *fp=fopen(path.c_str(),"wb+");
if(fp==nullptr)
printf("接收文件出错!(R216)\n");
else {
memset(recv_msg,0,sizeof(recv_msg));
int cnt=0;
while((cnt=recv(server_sock_fd,recv_msg,BUFFER_SIZE,0)) && cnt>0){
if(fwrite(recv_msg,sizeof(char),cnt,fp)<cnt){
printf("接收文件出错!(R223)\n");
break;
}
memset(recv_msg,0,sizeof(recv_msg));
if(cnt!=BUFFER_SIZE) break;
}
printf("Receive successful!\n");
fclose(fp);
}
path.clear(); filename.clear();
}
else if(recv_msg[0]=='3' && recv_msg[1]==' '){
//视频传输尚未完成
}
}
else if(byte_num < 0){
printf("接受消息出错!\n");
}
else{
printf("服务器端退出!\n");
shutdown(server_sock_fd,SHUT_RDWR);
exit(0);
}
}
void Socket_client::receive()
{
if (FD_ISSET(server_sock_fd, &client_fd_set)){
memset(recv_msg,0,sizeof(recv_msg));
long byte_num = recv(server_sock_fd,recv_msg,BUFFER_SIZE,0);
if (byte_num > 0){
if (byte_num > BUFFER_SIZE){
byte_num = BUFFER_SIZE;
}
recv_msg[byte_num] = '\0';
recvmessage();
if(strcmp(recv_msg,"服务器加入的客户端数达到最大值!\n")==0){
exit(0);
}
}
else if(byte_num < 0){
printf("接受消息出错!\n");
}
else{
printf("服务器端退出!\n");
shutdown(server_sock_fd,SHUT_RDWR);
exit(0);
}
}
}
int main (){
Socket_client tcpclient;
tcpclient.init();
if (tcpclient.connect_s()){
while (true){
if(!tcpclient.start()){
continue;
}
else{
if(!tcpclient.sendmessage()) exit(0);
tcpclient.receive();
}
}
}
return 0;
}