基于Socket的TCP协议Linux聊天系统(即时聊天、传送文件)

4.1 需求分析

4.1.1 系统目的

完成一个Linux下的网络通信程序,该程序包括服务器和客户端两部分,且能够互相通信,传递消息,传送文件,即时聊天。

4.1.2 系统结构图

图4.1-1 聊天系统结构图

4.1.3 系统功能

该系统主要功能为发送接收消息和文件,次要功能为功能选择、退出等。

4.1.4 系统数据流图

图4.1-2 系统0层数据流图
该系统是服务器与客户端之间进行通信,同时涉及到文件读取与存储,故还与文件系统有交互,所以抽取出服务器、客户端、文件系统三个交互方。服务器和客户端之间通过系统可以进行消息、文件的收发,同时系统可以处理服务器和客户端的操作,如功能选择、退出等。
  图4.1-3 系统1层数据流图
系统需要进行初始化,完成连接,服务器可以选择需要的功能,与客户端进行通信,收发并处理数据,可以退出程序。客户端可以选择需要的功能,与服务器进行通信,收发并处理数据,可以退出程序。
图4.1-4 系统2层数据流图
系统初始化时服务器建立监听端口,用于与客户端建立连接,客户端会发送地址和端口信息,与服务器建立连接。服务器客户端均可通过功能选择选择收发消息或收发文件的功能。

4.2 概要设计

4.2.1 总体设计

系统分为服务器和客户端两个独立的程序,通过socket进行通信,采用IPv4,TCP协议进行传输。可以通过输入C或F进行选择功能,C为发送消息,F为发送文件。发送消息按下回车后,接收方能够显示收到的消息,发送文件时接收方接收完毕后会显示接收的文件名以及文件存储的位置,同时会反馈给发送方,发送方会显示对方已接收文件。可以通过发送消息模式输入quit退出系统。当选择模式时输入非C或非F,系统会告知用户暂未开通此功能,敬请期待,仍可重新选择功能。

4.2.2 系统SC图

图4.2-1 聊天系统SC图

4.3 详细设计

4.3.1 套接字设计

1. 服务器和客户端连接流程

服务器

  1. 程序初始化;

  2. 持续监听一个固定的端口;

  3. 收到Client的连接后建立一个socket连接;

  4. 与Client进行通信和信息处理;

  5. 接收Client通过socket连接发送来的数据,进行相应处理并返回结果;

  6. 通信结束后中断与Client的连接.

客户端

  1. 程序初始化;

  2. 链接到某个Server上,建立socket连接;

  3. 与Server进行通信和信息处理;

  4. 接收Server通过socket连接发送来的数据,进行相应处理;

  5. 通过socket连接向Server发送请求信息;

  6. 通信结束后中断与Server的连接[1].

2. 流程图

图4.3-1 C/S模式TCP通信流程图

3. 关键代码
int get_sockfd()
{  
​    struct sockaddr_in server_addr; 
​    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
​    {
​      fprintf(stderr,"创建套接字错误:%s\n\a",strerror(errno));     
​        exit(1);
​    }
​    else
​    {
​    printf("创建套接字成功!\n");

​    }  

​    /*sockaddr结构 */ 
​    bzero(&server_addr,sizeof(server_addr));
​    server_addr.sin_family=AF_INET;     
​    server_addr.sin_port=htons(SERVER_PORT);    
​    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
​    int opt = 1;
​    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
​    /*绑定服务器的ip和服务器端口号*/
​    if(bind(sockfd,(struct sockaddr *)(&server_addr), sizeof(server_addr)) == -1){   
​        fprintf(stderr,"端口绑定错误:%s\n\a",strerror(errno));    
​        exit(1);   
​    } 
​    else{
​        printf("端口绑定成功!\n");  
​    } 
​     /* 设置允许连接的最大客户端数 */   
​    if(listen(sockfd,BACKLOG)==-1){  
​        fprintf(stderr,"监听端口错误:%s\n\a",strerror(errno));
​        exit(1); 
​    } 
​    else{
​        printf("端口监听中......\n"); 
​    } 
​    return sockfd;
}

4.3.2 发送消息

1. 流程图

图4.3-2 发送消息流程图

2. 关键代码
void sendMsg(char sendbuf[], int socket)
{
​    printf("你:");
​    scanf("%s", sendbuf);
​    if(send(socket, sendbuf, strlen(sendbuf), 0) < 0)
​    {
​        printf("消息发送失败!\n");
​    }
}

4.3.3 接收消息

1. 流程图

图4.3-3 接收消息流程图

2. 关键代码
void receiveMsg(char recvbuf[], char their_name[], int socket)
{
​    int iDataNum;
​    printf("%s:", their_name);
​    recvbuf[0] = '\0';
​    if((iDataNum = recv(socket, recvbuf, SIZE, 0)) < 0)
​    {
​        printf("数据接收失败!\n");
​    }
​    recvbuf[iDataNum] = '\0';
​    printf("%s\n", recvbuf);
} 

4.3.4 发送文件

1. 流程图

图4.3-4 发送文件流程图

2. 关键代码
int sendFile(char sendbuf[], char file_name[], int socket)
{
  // 打开文件并读取文件数据
   FILE *fp = fopen(file_name, "r");
  if(NULL == fp)
  {
​    printf("%s 文件不存在!\n", file_name);
  }
  else
  {
​    bzero(sendbuf, SIZE);
   int length = 0;
   // 每读取一段数据,便将其发送给服务器,循环直到文件读完为止
   while((length = fread(sendbuf, sizeof(char), SIZE, fp)) > 0)
   {
​     if(send(socket, sendbuf, length, 0) < 0)
​     {
​      printf("%s 发送失败\n", file_name);
​      break;
​     }
​     bzero(sendbuf, SIZE);
   }
   
   fclose(fp);
   printf("%s 发送成功!\n", file_name);
   int iDataNum;
   char recvbuf[SIZE];
   bzero(recvbuf,SIZE);
   recvbuf[0] = '\0';
   //接收对方接收完文件后的反馈信息
   if((iDataNum = recv(socket, recvbuf, SIZE, 0)) < 0)
   {
​      printf("数据接收失败!\n");
   }
   recvbuf[iDataNum] = '\0';
   if(strcmp(recvbuf, "success")==0)
   {
​       printf("对方已接收文件!\n");
   }
  }
  return 0;
}

4.3.5 接收文件

1. 流程图

图4.3-5 接收文件流程图

2. 关键代码
int receiveFile(char recvbuf[], char file_name[], int socket)
{
  // 打开文件,准备写入
  FILE *fp = fopen(file_name, "w");
  if(NULL == fp)
  {
​    printf("无法写入文件%s.\n", file_name);
​    exit(1);
  }
  // 从服务器接收数据到recvbuf中
  // 每接收一段数据,便将其写入文件中,循环直到文件接收完并写完为止
  bzero(recvbuf, SIZE);
  int length = 0;
  while(1)
  {
​    length = recv(socket, recvbuf, SIZE, 0);
​    if(length > 0){
​      if(fwrite(recvbuf, sizeof(char), length, fp) < length)
​      {
​        printf("写入文件%s失败\n", file_name);
​        break;
​      }
​      bzero(recvbuf, SIZE);
​     }
​     break;
   }        
 
   fclose(fp);
   printf("成功接收文件:%s! 文件存储在默认文件夹下(程序所在文件夹).\n", file_name);
   char sendbuf[SIZE]="success";
   send(socket, sendbuf, strlen(sendbuf), 0);
   return 0;
}

4.4 系统运行结果

4.4.1 启动程序、建立连接

图4.4-1 启动服务器
图4.4-2 启动客户端
图4.4-3 服务器与客户端建立连接

4.4.2 发送消息、即时聊天

图4.4-4 客户端发送消息
图4.4-5 服务器接收消息
图4.4-6 服务器发送消息
图4.4-7 客户端接收消息

4.4.3 发送文件、接收文件

图4.4-8 客户端发送client_test.txt文件
图4.4-9 服务器接收client_test.txt文件
图4.4-10 服务器接收到的文件和客户端发送的文件进行比较
图4.4-11 客户端发送其他格式文件
图4.4-12 服务器接收其他格式文件
图4.4-13 服务器所在文件夹接收结果
图4.4-14 服务器发送server_test.txt文件
图4.4-15 客户端接收server_test.txt文件
图4.4-16 服务器发送其他格式文件
图4.4-17 客户端接收其他格式文件
图4.4-18 客户端程序所在文件夹接收结果

4.4.4 功能选择

图4.4-19 功能选择

4.4.5 退出

图4.4-20 客户端退出
图4.4-21 服务器退出
源码地址:https://github.com/Marigoldwu/Chat

  • 22
    点赞
  • 123
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论
设计思路: Linux基于TCP协议聊天程序的设计与实现,需要考虑以下几个方面: 1. 通信协议:使用TCP协议进行通信,保证可靠性和稳定性。 2. 服务器端:需要设计一个服务器端,用于接收客户端的连接请求,创建连接并进行消息的转发。 3. 客户端:需要设计一个客户端,用于连接服务器,发送消息和接收消息。 4. 用户界面:需要设计一个简单易用的用户界面,方便用户进行聊天交流。 实现步骤: 1. 设计服务器端 服务器端需要监听客户端的连接请求,并创建与客户端的连接。在连接建立后,需要不断接收客户端发送的消息,并将消息转发给其他客户端。 2. 设计客户端 客户端需要连接服务器,并发送和接收消息。可以通过命令行或者形界面等方式实现。 3. 实现通信协议 使用TCP协议进行通信,可以使用Linux系统提供的socket接口实现。需要注意数据传输的大小和顺序,以及消息的分割和组装。 4. 实现用户界面 设计一个简单易用的用户界面,可以使用命令行或者形界面等方式实现。用户界面需要提供发送和接收消息的功能,同时可以显示其他用户发送的消息。 5. 测试和调试 完成程序的编写后,需要进行测试和调试,验证程序的正确性和稳定性。可以使用多个客户端连接服务器,进行消息的发送和接收,测试程序的并发性和性能。同时需要注意异常情况的处理,例如网络断开、连接异常等情况。 代码实现: 以下是一个简单的Linux基于TCP协议聊天程序的代码实现,包括服务器端和客户端的代码。需要注意的是,这只是一个简单的示例,实际应用中需要根据具体需求进行修改和完善。 服务器端代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #define MAX_CLIENT_NUM 10 #define BUFFER_SIZE 1024 void error_handling(char *message); int main(int argc, char *argv[]) { int server_sock, client_sock; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_size; char buffer[BUFFER_SIZE]; int client_socks[MAX_CLIENT_NUM] = {0}; int client_num = 0; int i, j; if (argc != 2) { printf("Usage: %s <port>\n", argv[0]); exit(1); } server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (server_sock == -1) { error_handling("socket() error"); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(atoi(argv[1])); if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { error_handling("bind() error"); } if (listen(server_sock, 5) == -1) { error_handling("listen() error"); } while (1) { client_addr_size = sizeof(client_addr); client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size); if (client_sock == -1) { error_handling("accept() error"); } client_socks[client_num++] = client_sock; for (i = 0; i < client_num; i++) { if (client_socks[i] == 0) { continue; } if (client_sock != client_socks[i]) { sprintf(buffer, "User %d connected.\n", client_sock); write(client_socks[i], buffer, strlen(buffer)); } } while (1) { memset(buffer, 0, BUFFER_SIZE); int read_len = read(client_sock, buffer, BUFFER_SIZE); if (read_len <= 0) { sprintf(buffer, "User %d disconnected.\n", client_sock); for (j = 0; j < client_num; j++) { if (client_sock == client_socks[j]) { client_socks[j] = 0; break; } } for (j = 0; j < client_num; j++) { if (client_socks[j] == 0) { continue; } write(client_socks[j], buffer, strlen(buffer)); } break; } for (i = 0; i < client_num; i++) { if (client_socks[i] == 0) { continue; } if (client_sock != client_socks[i]) { write(client_socks[i], buffer, strlen(buffer)); } } } } close(server_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUFFER_SIZE 1024 void error_handling(char *message); int main(int argc, char *argv[]) { int sock; struct sockaddr_in server_addr; char buffer[BUFFER_SIZE]; int str_len; if (argc != 3) { printf("Usage: %s <ip> <port>\n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { error_handling("socket() error"); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(argv[1]); server_addr.sin_port = htons(atoi(argv[2])); if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { error_handling("connect() error"); } while (1) { memset(buffer, 0, BUFFER_SIZE); printf("Send message: "); fgets(buffer, BUFFER_SIZE, stdin); if (strlen(buffer) <= 1) { continue; } write(sock, buffer, strlen(buffer)); memset(buffer, 0, BUFFER_SIZE); str_len = read(sock, buffer, BUFFER_SIZE - 1); if (str_len == -1) { error_handling("read() error"); } printf("Received message: %s", buffer); } close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } ```
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小吴不会敲代码吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值