计算机网络课程设计-Socket网络通信(C++, MacOS)

实验要求:

利用Socket实现双机通信

实验目的:

利用Socket编程,采用其中的TCP面向连接方式,实现计算机数据的交换

具体要求:

  1. 操作系统:可在Linux或Windows操作系统下实现
  2. 编程语言:C或C++,若使用Java、python等更高级的语言或脚本语言则须实现下面的“扩展功能”
  3. 界面要求:图形界面或者命令行界面均可
  4. 连接方式:局域网内通信,有线或无线网络均可
  5. 实现功能:
    • 基本功能(必做):计算机A、B(须为不同的物理机)实现文本通信,即B收到A所发送的文本并显示。
    • 扩展功能(加分选做):双向通信(同一程序下,A、B既可以发送数据又可以接受数据);文本传输(B完整收到A发送的文件并保存);实现音视频传输(类似于QQ电话);其他网络拓展功能
  6. 验收方式等不再赘述

    实现:

    基于C++和MacOS的实现,unix上可以正常运行,linux好像有库不支持。有关Socket的基础知识见博客网络socket编程指南,下面代码中要实现的内容有:
  7. 基本文本双向通信,多对多双向通信,实现私聊和群聊功能
  8. 文件双向传输,多对多双向传送,实现私传和群传功能

题目要求使用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

在这里插入图片描述

完整代码

服务端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
#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;
}
客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#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;
}