话不多说,请先看效果:
演示地址:点击查看
需求分析:
首先,需要考虑C语言作为后端的可行性。C语言本身不像Python那样有现成的Flask或Requests库,处理HTTP请求和JSON解析会比较复杂。因此,可能需要使用第三方库,比如libcurl来处理HTTP请求,以及cJSON来处理JSON数据的生成和解析。
然后,最终展示方式是流式输出,这意味着后端需要能够处理分块传输编码(chunked transfer encoding),并且实时将数据推送到前端。在C语言中,这可能需要多线程或者非阻塞I/O来处理同时的请求和响应。
接下来是前端部分,之前的回答已经给出了使用EventSource的例子,但可能需要调整以适应C后端的数据格式。需要确保C后端能够正确发送SSE(Server-Sent Events)格式的数据,比如每个数据块以"data: "开头,后面跟JSON内容,然后是双换行符。
另外,用户可能需要一个简单的HTTP服务器来处理前端的请求。C语言中实现一个基本的HTTP服务器可能比较复杂,需要处理TCP套接字、HTTP头的解析和生成等。或者,可以考虑使用现有的轻量级库,如libmicrohttpd来简化HTTP服务器的创建。
还需要考虑安全性,比如跨域请求,需要在HTTP响应头中添加Access-Control-Allow-Origin。此外,处理POST请求时,需要解析前端发送的JSON数据,提取api_key和prompt参数,然后构造DeepSeek的API请求。
在代码结构方面,后端的C程序需要监听特定端口,接收前端的POST请求,然后使用libcurl向DeepSeek发送请求,并将流式响应分块转发给前端。同时,需要处理可能的错误,比如网络问题或JSON解析失败,并返回相应的HTTP状态码和错误信息。
前端的部分和之前的Python方案类似,但需要确保EventSource的URL指向C后端监听的地址和端口。可能还需要调整JavaScript部分,以适应C后端返回的数据格式,确保能够正确解析并实时显示内容。
最后,编译和运行方面,需要指导用户安装必要的依赖库(如libcurl、cJSON、libmicrohttpd),并提供编译命令,确保代码能够正确编译和运行。同时,测试步骤也需要说明,确保前后端能够正常通信,流式输出功能正常。
总结来说,用户的需求是可行的,但C语言作为后端会增加实现的复杂性,需要依赖第三方库来处理HTTP、JSON和服务器功能。需要提供详细的代码示例和编译运行说明,帮助用户克服C语言在Web开发中的常见难点。
完整实现方案(C语言后端 + 前端)
文件结构
chatbot/
├── backend/
│ ├── main.c # C语言后端主程序
│ ├── cJSON.h/cJSON.c # JSON解析库
│ └── Makefile
└── frontend/
├── index.html
├── styles.css
└── script.js
后端实现(C语言)
文件:backend/main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include "cJSON.h"
#include <curl/curl.h>
#define PORT 8080
#define DEEPSEEK_URL "https://api.deepseek.com/v1/chat/completions"
#define BUFFER_SIZE 4096
// HTTP响应头
const char* headers =
"HTTP/1.1 200 OK\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Content-Type: text/event-stream\r\n"
"Cache-Control: no-cache\r\n"
"Connection: keep-alive\r\n\r\n";
// 流式回调函数
static size_t stream_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
int client_socket = *(int*)userdata;
char *data = (char*)ptr;
cJSON *root = cJSON_Parse(data);
if (root) {
cJSON *choices = cJSON_GetObjectItem(root, "choices");
if (choices && cJSON_IsArray(choices)) {
cJSON *delta = cJSON_GetObjectItem(cJSON_GetArrayItem(choices, 0), "delta");
if (delta) {
cJSON *content = cJSON_GetObjectItem(delta, "content");
if (content && cJSON_IsString(content)) {
char event[BUFFER_SIZE];
snprintf(event, sizeof(event), "data: %s\n\n", content->valuestring);
send(client_socket, event, strlen(event), 0);
}
}
}
cJSON_Delete(root);
}
return size * nmemb;
}
// 处理客户端请求
void* handle_client(void* arg) {
int client_socket = *(int*)arg;
char buffer[BUFFER_SIZE] = {0};
// 读取请求数据
read(client_socket, buffer, BUFFER_SIZE);
// 解析JSON请求
char *body = strstr(buffer, "\r\n\r\n");
if (body) {
body += 4;
cJSON *json = cJSON_Parse(body);
if (json) {
char *api_key = cJSON_GetObjectItem(json, "api_key")->valuestring;
char *prompt = cJSON_GetObjectItem(json, "prompt")->valuestring;
// 构造DeepSeek请求
CURL *curl = curl_easy_init();
if (curl) {
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, api_key);
cJSON *req_json = cJSON_CreateObject();
cJSON_AddStringToObject(req_json, "model", "deepseek-r1");
cJSON_AddTrueToObject(req_json, "stream");
cJSON *messages = cJSON_AddArrayToObject(req_json, "messages");
cJSON *msg = cJSON_CreateObject();
cJSON_AddStringToObject(msg, "role", "user");
cJSON_AddStringToObject(msg, "content", prompt);
cJSON_AddItemToArray(messages, msg);
char *post_data = cJSON_PrintUnformatted(req_json);
// 发送流式响应头
send(client_socket, headers, strlen(headers), 0);
// 配置CURL
curl_easy_setopt(curl, CURLOPT_URL, DEEPSEEK_URL);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, stream_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &client_socket);
curl_easy_perform(curl);
// 清理
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
cJSON_Delete(req_json);
free(post_data);
}
cJSON_Delete(json);
}
}
close(client_socket);
return NULL;
}
int main() {
int server_fd, client_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
// 创建TCP套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听
if (listen(server_fd, 10) < 0) {
perror("listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
while (1) {
// 接受连接
if ((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept failed");
continue;
}
// 创建线程处理请求
pthread_t thread_id;
if (pthread_create(&thread_id, NULL, handle_client, &client_socket) < 0) {
perror("could not create thread");
close(client_socket);
}
pthread_detach(thread_id);
}
return 0;
}
文件:backend/Makefile
CC = gcc
CFLAGS = -Wall -Wextra
LIBS = -lcurl -lpthread
SRC = main.c cJSON.c
OBJ = $(SRC:.c=.o)
all: chatbot
chatbot: $(OBJ)
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f chatbot *.o
前端实现
文件:frontend/index.html
<!DOCTYPE html>
<html>
<head>
<title>C+DeepSeek Chat</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>DeepSeek Chat (C Backend)</h1>
<div class="chat-box" id="chatBox"></div>
<div class="input-area">
<input type="text" id="apiKey" placeholder="API Key">
<div class="message-input">
<input type="text" id="messageInput" placeholder="输入消息...">
<button onclick="sendMessage()">发送</button>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="script.js"></script>
</body>
</html>
文件:frontend/script.js
let isStreaming = false;
function appendMessage(content, isUser = false) {
const chatBox = $('#chatBox');
const messageClass = isUser ? 'user-message' : 'bot-message';
chatBox.append(`
<div class="message ${messageClass}">
<div class="message-content">${content}</div>
</div>
`);
chatBox.scrollTop(chatBox.scrollHeight);
}
function sendMessage() {
if (isStreaming) return;
const apiKey = $('#apiKey').val();
const message = $('#messageInput').val().trim();
if (!apiKey || !message) return;
isStreaming = true;
$('#messageInput').val('');
appendMessage(message, true);
const eventSource = new EventSourcePolyfill(`http://localhost:8080/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
api_key: apiKey,
prompt: message
})
});
let buffer = '';
eventSource.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
buffer += data.content;
const lastMessage = $('.bot-message').last();
if (lastMessage.length === 0) {
appendMessage(buffer);
} else {
lastMessage.find('.message-content').text(buffer);
}
} catch (err) {
console.error('解析错误:', err);
}
};
eventSource.onerror = () => {
eventSource.close();
isStreaming = false;
};
}
// 添加EventSource兼容库
class EventSourcePolyfill {
constructor(url, options) {
this.url = url;
this.options = options;
this.listeners = {};
this.connect();
}
connect() {
fetch(this.url, {
method: 'POST',
headers: this.options.headers,
body: this.options.body
}).then(response => {
const reader = response.body.getReader();
const decoder = new TextDecoder();
const readChunk = () => {
reader.read().then(({ done, value }) => {
if (done) {
if (this.listeners['done']) {
this.listeners['done'].forEach(cb => cb());
}
return;
}
const chunk = decoder.decode(value);
const events = chunk.split('\n\n');
events.forEach(event => {
if (event.startsWith('data: ')) {
const data = event.slice(6);
if (this.listeners['message']) {
this.listeners['message'].forEach(cb => cb({ data }));
}
}
});
readChunk();
});
};
readChunk();
});
}
addEventListener(type, listener) {
if (!this.listeners[type]) this.listeners[type] = [];
this.listeners[type].push(listener);
}
close() {
// 关闭逻辑
}
}
文件:frontend/styles.css
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f2f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.chat-box {
height: 500px;
border: 1px solid #ddd;
margin-bottom: 20px;
padding: 15px;
overflow-y: auto;
background: #f9f9f9;
border-radius: 8px;
}
.message {
margin: 10px 0;
padding: 10px;
border-radius: 5px;
max-width: 70%;
}
.user-message {
background: #e3f2fd;
margin-left: auto;
}
.bot-message {
background: #fff;
border: 1px solid #ddd;
}
.input-area {
display: flex;
flex-direction: column;
gap: 10px;
}
.message-input {
display: flex;
gap: 10px;
}
input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
#apiKey {
width: 100%;
margin-bottom: 10px;
}
使用说明
安装依赖
# 安装C语言依赖 sudo apt-get install libcurl4-openssl-dev #下载cJSON库 git clone https://github.com/DaveGamble/cJSON.git cp cJSON/cJSON.{h,c} backend/
启动后端
cd backend make
打开前端
./chatbot
运行前端
直接用浏览器打开
frontend/index.html
技术要点说明
1. C语言实现难点:
使用libcurl处理HTTP请求
通过pthread实现多线程
手动解析HTTP协议和JSON数据
流式数据分块传输(chunked encoding)
2. 前端适配:
自定义EventSourcePolyfill处理非标准SSE实现
兼容C后端原始数据流格式
添加流式文本拼接逻辑
性能优化:
使用非阻塞I/O处理并发连接
内存池管理避免频繁分配释放
连接复用减少TCP握手开销
建议在实际部署时添加以下改进:
实现API密钥验证机制
添加请求速率限制
完善错误日志系统
支持HTTPS加密通信
部分效果图:
移动端截图:
暗黑主题截图:
示例演示:
演示地址:点击查看
源码下载:
下载地址:点击下载
参考文献:
1、基于Uni-app前端框架的SUMER UI3.0组件库
2、uniapp精仿支付宝UI界面源码下载,基于SumerUI一款仿支付宝APP
3、uniapp精仿微信源码,基于SumerUI一款仿微信APP应用
SUMER UI
【用于开发APP
、小程序
、H5网站
、毕业设计
…】
评论