【发布时间】:2021-05-18 15:50:39
【问题描述】:
我正在编写一个聊天应用程序,包括服务器端和客户端。为长代码道歉。大概是我误解了 TCP/IP 的整体,但我不知道从哪里开始。问题是,当多个客户端连接到服务器时,如果向服务器发送了许多消息,则不会立即将消息传递给客户端。我可以收集到许多消息被附加到服务器端套接字连接上的缓冲区,并且在任意数量的消息之后,该缓冲区被转储到其中一个客户端。此转储在客户端按 Enter 后立即发生,因此我认为多线程中也存在问题。
我的想法:
- 也许有一种替代方法可以在服务器端跟踪客户端连接,而不是使用文件。全局文件指针可能会更好!这看起来不错?
- TCP/IP 是纯数据流而不是数据包传输(逐个消息),所以我不应该这样对待。
- 学习 pthread,用途更广泛。
(另外,我在使用 GDB 附加功能进行调试时遇到了问题,因此我无法调试我的程序:/ 我正在运行 Ubuntu 20.04,我也将不胜感激)
客户:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]) {
int cli_sock, port;
struct sockaddr_in serv_addr;
char buffer[1024];
port = atoi(argv[1]);
if((cli_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket failed.\n");
exit(1);
}
printf("Client socket initialised.\n");
memset(&serv_addr, '\0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(cli_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("Error in connection.\n");
exit(1);
}
while(1) {
printf("Client: ");
fgets(buffer, 1024, stdin);
send(cli_sock, buffer, strlen(buffer), 0);
if(strcmp(buffer, "/leave") == 0) {
close(cli_sock);
printf("Disconnected from server.\n");
exit(1);
}
if(recv(cli_sock, buffer, 1024, 0) < 0) {
printf("Error in receiving data.\n");
}
else {
if(sizeof(*buffer) == 1) { // prints data for all but sender.
printf("%s", buffer);
}
}
}
return 0;
}
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int fileLineCount(FILE *);
void deleteSockFromFile(FILE *, int);
int main(int argc, char *argv[]) {
int* senderID = calloc(1, sizeof(int));
int serv_sock, port;
struct sockaddr_in serv_addr;
int new_sock;
struct sockaddr_in new_addr;
socklen_t addrlen;
char buffer[1024];
int roomSize;
port = atoi(argv[1]);
roomSize = atoi(argv[2]);
if((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Server socket failed.\n");
exit(1);
}
memset(&serv_addr, '\0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(bind(serv_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
printf("Bind unsuccessful.\n");
exit(1);
}
if(listen(serv_sock, roomSize) != 0) {
printf("Listening...\n");
}
else {
perror("Listening failed.\n");
exit(1);
}
memset(buffer, '\0', sizeof(buffer));
FILE *clientsFile = NULL;
if(!(clientsFile = fopen("clients.txt", "w+"))) { // file for tracking active client connections (socket fds)
perror("Failed to open file.\n");
exit(1);
}
while(1) {
new_sock = accept(serv_sock, (struct sockaddr *) &new_addr, &addrlen);
if(new_sock < 0) { //handling failed socket creation.
perror("Socket failed.\n");
exit(1);
}
printf("Connection accepted from %s:%d. Sock fd = %d\n", inet_ntoa(new_addr.sin_addr), ntohs(new_addr.sin_port), new_sock);
printf("lineCount: %d\n", fileLineCount(clientsFile));
fseek(clientsFile, 0, SEEK_END);
fprintf(clientsFile, "%d\n", new_sock);
printf("lineCount: %d\n", fileLineCount(clientsFile));
if(fork() == 0) {
close(serv_sock);
while(1) {
memset(buffer, '\0', sizeof(buffer));
recv(new_sock, buffer, 1024, 0);
if(strcmp(buffer, "/leave") == 0) {
printf("Disconnected from %s:%d.\n", inet_ntoa(new_addr.sin_addr), ntohs(new_addr.sin_port));
deleteSockFromFile(clientsFile, new_sock);
break;
}
else {
printf("Client: %s", buffer);
int temp_sock;
size_t length = 10;
char *line = calloc(length, sizeof(char));
rewind(clientsFile);
while(getline(&line, &length, clientsFile) >= 0) { // reads file with client sock fd
line[strcspn(line, "\n")] = 0;
temp_sock = atoi(line); // set temp_sock to a client sock fd
if(temp_sock != new_sock) { // compares thread value of new_sock with all client socks, so the sender isn't sent the message, but everyone else is.
send(temp_sock, buffer, strlen(buffer), 0);
printf("SHOULD SEND TO %d\n", temp_sock); //debugging line #1/2
}
else {
send(temp_sock, senderID, sizeof(senderID), 0);
printf("Should not send, cause sender.\n"); //debugging line #2/2
}
}
}
}
break;
}
}
fclose(clientsFile);
remove("clients.txt");
close(new_sock);
}
int fileLineCount(FILE *ptr) {
int ch, lineCount = 0;
fseek(ptr, 0, SEEK_SET);
do {
ch = fgetc(ptr);
if(ch == '\n')
lineCount++;
}
while (ch != EOF);
return lineCount;
}
void deleteSockFromFile(FILE *ptr, int sock2delete) {
size_t length = 10;
char *line = calloc(length, sizeof(char));
char *tempLine = calloc(length, sizeof(char));
FILE *tempPtr = NULL;
if((tempPtr = fopen("temp.txt", "w")) == 0) {
perror("Unable to open file.\n");
exit(1);
}
rewind(ptr); // must reset file cursor before switching from writing to reading mode (w+)
while(getline(&line, &length, ptr) >= 0) {
strcpy(tempLine, line);
tempLine[strcspn(tempLine, "\n")] = 0; //remove newline char
if(atoi(tempLine) != sock2delete) {
fputs(line, tempPtr);
}
}
remove("clients.txt");
rename("temp.txt", "clients.txt");
fclose(tempPtr);
free(line);
free(tempLine);
}
这里是上下文的测试输出,测试并不顺利,但你可以看到只有一小部分消息通过(看@左上角终端,“c2”是从客户端 2 收到的)@987654321 @
(也许这个问题应该重命名为 GDB attach 导致笔记本电脑在使用 fork() 在多线程程序上使用 sudo 运行时冻结,感谢您的想法!)
【问题讨论】:
-
if(sizeof(*buffer) == 1)始终为真。 -
从服务器接收的缓冲区是char指针或int指针,“buffer”是一个不清楚的名字我很抱歉,但是当“buffer”是一个int指针时,大小是4,不是1.
-
buffer[]是一个字符数组,所以buffer[0](等于*buffer)是一个字符,与已收到(未收到)的内容无关。 -
if(strcmp(buffer, "/leave") == 0) {将永远为真(fgets() 保留\n) -
您还没有阅读服务器端代码。所有这些行都按预期工作。
标签: c multithreading sockets