c - 通过套接字发送/接收数据时出现问题
问题描述
我正在使用 C 中的 ncurses UI 开发一个简单的聊天程序,并且遇到了这个奇怪的问题。
它是这样的:
服务器等待客户端连接。
当客户端连接时,服务器会提示输入用户名。
收到用户名后,服务器开始监听来自客户端的消息。
每次服务器接收到一条消息,它都会将它与用户名连接起来,并将其广播给所有客户端,因此客户端接收到的消息应该如下所示:
username: text
这是出问题的地方。
客户端输出:
test^U^D: hello waddup
所以,基本上,接收数组的前六个索引总是用这些转义字符填充,如果用户名短于 6 个字母,这些转义字符会打印在客户端。
像这样:
USERNAME: a //user input
YOU: test message a^C^\^?^U^D: test message
在调查这个问题时,我在服务器端添加了调试消息。
服务器输出:
USERNAME RECEIVED:
: message
//tested one-letter username - q
USERNAME RECEIVED: q
q: asd
//two-letter - qw
USERNAME RECEIVED: as
as: sdf
//three-letter - asd. Still doesn't print the last letter, eventhough on client side it is printed, probably some stupid mistake I've made and didn't notice yet
USERNAME RECEIVED: test
test: hello
//Finally prints full u-name. No escape characters like on client side.
//If u-name >= 4 let. it is displayed normally on server side. If uname >= 6 -- on client side
我的猜测是套接字层上出现了问题,但我对套接字编程的了解不足,无法对我的建议充满信心。
或者,该问题可能与 ncurses UI 相关联,因为它在客户端(使用 ncurses)上可见,而在服务器端(将调试消息打印到 sdout)不可见。
我知道它可能真的很乱而且效率很低,但是这个项目是我第一个比较认真的 C 项目,以后我肯定会重构和优化这段代码。
服务器端有问题的功能代码:
void transmitt(int socket, int index){
printf("CONNECTION TO THREAD %x\n", pthread_self());
char tmp[MAX];
char bye[MAX];
char uname[MAX_UNAME];
char buffer[MAX+MAX_UNAME];
sprintf(bye, "%d", BYE_MESSAGE);
recv_uname(socket, uname);
while(1){
bzero(tmp, sizeof(tmp));
bzero(buffer, sizeof(buffer));
recv(socket, tmp, sizeof(tmp), 0);
if(strncmp(bye, tmp, MAX) == 0){
printf("THREAD: %x DISCONNECTED\n", pthread_self());
snprintf(buffer, sizeof(buffer), "SERVER MESSAGE: %s HAS DISCONNECTED", uname);
for(int i =0;i<MAX_CLIENTS;i++){
if(clients[i] != NULL){
send(clients[i], buffer, sizeof(buffer), 0);
}
}
clients[index] = NULL;
return;
}
snprintf(buffer, sizeof(buffer), "%s: %s", uname, tmp);
for(int i =0;i<MAX_CLIENTS;i++){
if(clients[i] != NULL){
send(clients[i], buffer, sizeof(buffer), 0);
}
}
printf("%s\n", buffer);
}
}
void recv_uname(int socket, char* uname){
char prompt[MAX_UNAME];
snprintf(prompt, sizeof(prompt), "USERNAME: ");
send(socket, prompt, sizeof(prompt), 0);
recv(socket, uname, MAX_UNAME, 0);
printf("USERNAME RECEIVED: %s\n", uname);
}
客户端:
void chat_loop(int socket){
char buff[MAX], sbuff[MAX+MAX_UNAME];
char tmp;
int n, y=0, x=0, maxcol, maxrow, recy, recx;
getmaxyx(stdscr, maxrow, maxcol);
recy = 0;
recx = maxcol/2;
timeout(1);
bzero(buff, sizeof(buff));
printw("YOU: ");
n=0;
while(1){
tmp = getch();
getyx(stdscr, y, x);
if(tmp == '`'){
sprintf(buff, "%d", BYE_MESSAGE);
send(socket, buff, sizeof(buff), 0);
return;
}
else if(tmp == '\n'){
send(socket, buff, sizeof(buff), 0);
bzero(buff, sizeof(buff));
// printw("YOU: ");
move(++y, 0);
n=0;
}
else if(tmp == ALT_BACKSPACE){
if(x > 0){
mvdelch(y, x-1);
buff[--n] = NULL;
}
}
else if(tmp != ERR){
printw("%c", tmp);
buff[n++] = tmp;
}
bzero(sbuff, sizeof(sbuff));
recv(socket, sbuff, sizeof(sbuff), MSG_DONTWAIT);
if(check_buff(sbuff, sizeof(sbuff)) == 0){
getyx(stdscr, y, x);
move(recy++, recx);
printw("%s", sbuff);
move(y, x);
}
refresh();
}
}
void send_uname(int socket){
char buff[MAX], unbuff[MAX_UNAME];
char tmp;
int n=0, y=0, x=0, init_x;
bzero(buff, sizeof(buff));
recv(socket, buff, sizeof(buff), 0);
printw("%s", buff);
getyx(stdscr, y, init_x);
while((tmp = getch())!='\n'){
getyx(stdscr, y, x);
if(tmp == ALT_BACKSPACE){
if(x > init_x){
mvdelch(y, x-1);
unbuff[--n] = NULL;
}
}
else if(tmp != ERR){
if(n < MAX_UNAME){
printw("%c", tmp);
unbuff[n++] = tmp;
}
}
refresh();
}
printw("\n");
send(socket, unbuff, sizeof(unbuff), 0);
}
int check_buff(char* buffer, size_t n){
for(int i=0;i<n;i++){
if(buffer[i]!=NULL)
return 0;
}
return -1;
}
常量和包括:
#include<stdio.h>
#include<stdlib.h>
#include <netdb.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<ncurses.h>
//common
#define ALT_BACKSPACE 127
#define MAX 128 //common buffer size
#define MAX_UNAME 24
#define BYE_MESSAGE 10101010
//server-specific
#define MAX_THREADS 4
#define MAX_CLIENTS 4
这就是我所拥有的。
解决方案
推荐阅读
- django - 关注/关注系统 Django - 如何检查用户是否已经关注
- gstreamer - 如何配置 gstreamer 多队列以确保我们从每个源中抓取相同数量的帧
- php - PHP安装:错误:无法切换模块的启用流
- python - 尝试匹配多个参数化测试时,pytest -k 表达式失败
- security - 原始代码的 SAST 更好或编译代码
- java - 是否有一种简化的方法可以从 rust 调用 java 函数?
- laravel - 在流明中获取未定义的函数 public_path() 错误
- javascript - 如何使用 express.js 将自定义标头添加到 socket.io 的响应中?
- python - 错误元素在点 (183, 798) web 抓取 python selenium 处不可点击
- c++11 - 我在此合并排序算法代码中遇到运行时错误