實驗要求:
利用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;
}