C - 通过套接字发送 UDP 消息客户端到客户端
C - Sending UDP messages client-client through a socket
我有一个简单的问题。 至少我觉得很简单!
我正在做一个大学项目,我已经构建了一个服务器-客户端即时消息服务,结构如下:
-服务器创建一个TCP套接字来监听客户端请求
- 除了其他功能外,每个客户端都可以向另一个客户端发送消息(甚至离线)使用 UDP
我已将端口 4242 用于服务器,然后尝试将 4243 和 4244 用于两个示例客户端。
我的问题是:这些端口是否不接受 table 用于 UDP 通信?
我问是因为当我尝试向这些端口发送消息时,接收客户端获取并打印 "strange" 字符串。
准确地说,它存储了先行消息并首先打印新消息,然后是一大块先行消息。
另一方面,如果我使用例如 4303 和 4486,它会按预期工作。
我在查看 Wikipedia 时得到了这些数字,但由于我不确定如何解释 table(所以,如果我解释正确的话)并且因为我知道维基百科可以不太靠谱,我想我可以在这里问。
附带说明一下,如果我提供的 link 不可靠,是否有任何命令或资源可以检查这些东西?
提前致谢!
编辑:
这是我的代码!之前上传不了。。我也尽量简单说明一下。
正如我在评论中提到的,我以为我收到了,但一个客户没有收到另一个客户的消息。所以一切正常,除了 !send 功能,我猜...
客户端
客户端是使用“!注册用户名”连接到服务器的用户;这个命令:
- 如果客户端是第一次连接到服务,则将客户端注册到该服务,当然将其设置为 "online"
-如果客户端已经注册到该服务,则重新连接客户端;在这种情况下,如果有一些离线消息,服务器会将其传送给客户端
连接后,客户端可以使用以下命令:
1)!help: 显示可用命令
2)!who: 显示在线人数
3)!deregister: 顾名思义,从服务器注销客户端
4)!send username:如果目的客户端在线,服务器returnsUDP目的地址和端口,客户端直接发送消息;如果目标客户端离线,服务器会存储消息,以便在目标客户端连接后将其发送给目标客户端
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#define DIM 1024
#define ADDR_DIM 16
#define PORT_DIM 5
#define CMD_DIM 12
#define NAME_DIM 21
void cl_help(int sckt) {
int rv, length, l;
char* text = "!help";
char answer[DIM];
length = strlen(text);
l = htons(length);
//Sending command dimension
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending the command itself
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
}
void cl_quit(int sckt) {
int rv, length, l;
char* text = "!quit";
char answer[DIM];
length = strlen(text);
l = htons(length);
//Sending command dimension
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending the command itself
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
}
void cl_who(int sckt) {
int rv, length, l, n_net, n, i;
char* text = "!who";
char answer[DIM];
length = strlen(text);
l = htons(length);
//Sending the dimension of the command
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending the command itself
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if( (strcmp(answer, "Nessun utente in linea") != 0)\
&& (strcmp(answer, "Devi essere connesso per poter utilizzare questa funzione") != 0) ) { //No user online; Yuo have to be registered
//Receiving number of users online
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) &n_net, length, 0);
if(rv < 0) {};
n = ntohs(n_net);
//Getting every username
for(i=0; i<n; i++) {
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
};
};
}
void cl_deregister(int sckt) {
int rv, length, l;
char* text = "!deregister";
char answer[DIM];
//Sending command dimension
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Receiving answer
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
}
void cl_register(int sckt, char* name, char* port) {
int rv, length, l, n, n_net, i;
char msg[DIM+NAME_DIM+4];
char answer[DIM];
sprintf(msg, "!register %s %s", name, port);
//Sending command
length = strlen(msg);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) msg, length, 0);
if(rv < 0) {};
//Receiving answer
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if(strcmp(answer, "L'utente era gia' registrato al servizio: riconnessione completata.\n") == 0) { //Users already registered: reconnection
//Receiving info
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if(strcmp(answer, "Messaggi offline presenti.") == 0) { //Offline messages found
//Receiving the number of messages
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) &n_net, length, 0);
if(rv < 0) {};
n = ntohs(n_net);
//Recevingi messages
for(i=0; i<n; i++) {
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) msg, length, 0);
if(rv < 0) {};
msg[length] = '[=10=]';
puts(msg);
};
};
};
}
void cl_send(int sckt, int udp_sd, char* name, char* info, char* source) {
struct sockaddr_in dest;
int rv, length, l,p;
char msg[DIM+NAME_DIM+4];
char address[ADDR_DIM];
char port[PORT_DIM];
char answer[DIM];
char *pointer;
sprintf(msg, "!send %s", name);
//Sending command
length = strlen(msg);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) msg, length, 0);
if(rv < 0) {};
//Receiving answer
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if(strcmp(answer, "Devi essere connesso per poter utilizzare questa funzione.\n") != 0) { //You have to be registered
if(strncmp(answer, "Client attualmente connesso", 27) == 0) { //User connected right now
pointer = answer+29; //to get only the address and the port number
sscanf(pointer, "%s %s", address, port);
address[ADDR_DIM] = '[=10=]';
port[PORT_DIM] = '[=10=]';
p = ntohs(atoi(port));
memset(&dest, 0, sizeof(dest));
memset(&msg, 0, sizeof(msg));f
//Destination UDP socket
dest.sin_family = AF_INET;
dest.sin_port = htons(p);
inet_pton(AF_INET, address, &dest.sin_addr);
//Sending direct message using UDP
sprintf(msg, "%s > ", source);
strcat(msg, info);
length = strlen(msg);
l = htons(length);
//Sending message
rv = sendto(udp_sd, (void*) &l, sizeof(l), 0, (struct sockaddr*) &dest, sizeof(dest));
if(rv < 0) {};
rv = sendto(udp_sd, (void*) msg, length, 0, (struct sockaddr*) &dest, sizeof(dest));
if(rv < 0) {};
} else { //Offline transmission: transmits message to server using TCP
length = strlen(info);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) info, length, 0);
if(rv < 0) {};
//Getting info from the server
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
};
};
}
int main(int argc, char* argv[]) {
struct sockaddr_in srv_addr;
struct sockaddr_in my_addr;
int sd, udp;
int err;
int pid;
int length, l;
char buffer[DIM];
char cmd[CMD_DIM];
char dest[NAME_DIM];
char my_name[NAME_DIM];
char in[DIM];
char to_send[DIM+NAME_DIM+4];
char part[DIM] = "";
if(argc != 5) {
//Check argument
exit(EXIT_FAILURE);
};
//TCP socket
sd = socket(AF_INET, SOCK_STREAM, 0);
//UDP socket
udp = socket(AF_INET, SOCK_DGRAM, 0);
//Pulizia
memset(&srv_addr, 0, sizeof(srv_addr));
memset(&my_addr, 0, sizeof(my_addr));
//TCP server parameters
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(atoi(argv[4]));
inet_pton(AF_INET, argv[3], &srv_addr.sin_addr);
//my UDP parameters
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET, argv[1], &my_addr.sin_addr);
//Bind for UDP socket
err = bind(udp, (struct sockaddr*) &my_addr, sizeof(my_addr));
if(err < 0) {};
//Connecting to the server
err = connect(sd, (struct sockaddr*) &srv_addr, sizeof(srv_addr));
if(err < 0) {};
//Auto-sending help command to display available operations
strcpy(cmd, "!help");
length = strlen(cmd);
l = htons(length);
err = send(sd, (void*) &l, sizeof(l), 0);
if(err < 0) {};
err = send(sd, (void*) cmd, length, 0);
if(err < 0) {};
//Receiving info from the server -> available operations
err = recv(sd, (void*) &l, sizeof(l), 0);
if(err < 0) {};
length = ntohs(l);
err = recv(sd, (void*) buffer, length, 0);
if(err < 0) {};
buffer[strlen(buffer)] = '[=10=]';
puts(buffer);
pid = fork();
if(pid == 0) {
//Child
close(sd);
while(1) {
memset(to_send, 0, sizeof(to_send));
err = recvfrom(udp, (void*) &l, sizeof(l), 0, 0, 0);
if(err < 0) {
puts("error recvfrom");
exit(EXIT_FAILURE);
};
length = ntohs(l);
err = recvfrom(udp, (void*) to_send, length, MSG_WAITALL, 0, 0);
if(err < 0) {
puts("error recvfrom");
exit(EXIT_FAILURE);
};
to_send[length] = '[=10=]';
puts(to_send);
};
} else {
//Parent
while(1) {
//Asking the user to prompt the command
memset(cmd, 0, sizeof(cmd));
memset(in, 0, sizeof(in));
memset(dest, 0, sizeof(dest));
puts("\nInserisci un comando: "); //Insert a command
fgets(in, DIM, stdin);
in[strlen(in)-1] = '[=10=]';
sscanf(in, "%s", cmd);
cmd[CMD_DIM-1] = '[=10=]';
if(strcmp(cmd, "!help") == 0) {
cl_help(sd);
continue;
};
if(strcmp(cmd, "!deregister") == 0) {
cl_deregister(sd);
};
if(strcmp(cmd, "!register") == 0) {
sscanf(in, "%s %s", cmd, dest);
cmd[CMD_DIM-1] = '[=10=]';
dest[NAME_DIM-1] = '[=10=]';
strcpy(my_name, dest);
if(strlen(dest) == 0) { //to avoid an empty username
puts("Errore: non hai inserito alcun username; riprova.\n"); //Please insert an username
} else {
cl_register(sd, dest, argv[2]);
};
continue;
};
if(strcmp(cmd, "!who") == 0) {
cl_who(sd);
continue;
};
if(strcmp(cmd, "!send") == 0) {
sscanf(in, "%s %s", cmd, dest);
cmd[CMD_DIM-1] = '[=10=]';
dest[NAME_DIM-1] = '[=10=]';
while(1) {
memset(part, 0, sizeof(part));
fgets(part, DIM-(strlen(to_send)-NAME_DIM-4), stdin);
l = strlen(part);
if((part[l-2] == '.') && (part[l-1] == '\n') && (strlen(part) == 2)) {
part[l-2] = '[=10=]';
break;
} else {
strcat(to_send, part);
};
if(strlen(to_send) >= DIM+NAME_DIM+3) {
to_send[DIM+NAME_DIM+3] = '[=10=]';
break;
};
};
strcat(to_send, "[=10=]");
cl_send(sd, udp, dest, to_send, my_name);
memset(to_send, 0, sizeof(to_send)); //to empty the buffer used to store the message sent
continue;
};
if(strcmp(cmd, "!quit") == 0) {
cl_quit(sd);
close(sd);
break;
} else{
puts("Comando non valido.\n"); //Not a valid command
};
};
};
return 0;
}
服务器
服务器响应来自客户端的请求并被构造为并发服务器。
#include <sys/mman.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#define MAX_CONN 100
#define DIM 1024
#define ADDR_DIM 16
#define CMD_DIM 12
#define NAME_DIM 20
#define MSG_DIM DIM+NAME_DIM+4
#define PORT_DIM 5
#define MAX_UTN 1024
struct users {
char username[NAME_DIM];
char status; //c = connected, o = offline
struct sockaddr_in address;
char pendent[DIM][DIM];
char sender[DIM][NAME_DIM];
int msg; //last message to be sent
};
void srv_help(int sckt) {
int rv, length, l;
char* text = "Sono disponibili i seguenti comandi: \n\
!help --> mostra l'elenco dei comandi disponibili \n\
!register username --> registra il client presso il server\n\
!deregister --> de-registra il client presso il server\n\
!who --> mostra l'elenco degli utenti disponibili\n\
!send username --> invia un messaggio ad un altro utente\n\
(messaggio subito dopo comando\n\
!quit --> disconnette il client dal server ed esce\n\
('username': max 20 caratteri)\n\
(per terminare messaggio inserisci un . da solo)\n";//info about the available commands
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
};
void srv_who(int sckt, struct users* reg, int last) {
int i, rv, length, l;
char text[DIM];
strcpy(text, "Nessun utente in linea\n");//No user online
if(last == 0) { //if no user is online
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
} else {
strcpy(text, "Utenti in linea:\n"); //Users online:
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Here I send the number of users online, to synchronize server and client
length = sizeof(last);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
l = htons(last);
rv = send(sckt, (void*) &l, length, 0);
if(rv < 0) {};
//Transmitting users
for(i=0; i<last; i++) {
if(reg[i].status == 'c') {
length = strlen(reg[i].username);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) reg[i].username, length, 0);
if(rv < 0) {};
};
};
};
};
void srv_deregister(int sckt, struct users* reg, char* name, int* last) {
int i, rv, length, l;
char text[DIM];
strcpy(text, "Deregistrazione completata.\n"); //De-registration completed
for(i=0; i<*last; i++) {
if(strcmp(reg[i].username, name) == 0) { //user found
reg[i] = reg[*last];
(*last)--;
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
return;
};
};
}
int srv_register(int sckt, struct users* reg, char* name, struct sockaddr_in client, int p, int* last) { //char*
int i, rv, length, l, j, pos;
//char pos[MSG_DIM]; // = "";
char text[DIM];
char buffer[MSG_DIM];
memset(text, 0, DIM);
strcpy(text, "Registrazione effettuata con successo.\n"); //Registration completed
for(i=0; i<*last; i++) {
if(strcmp(reg[i].username, name) == 0) { //User already registered: re-connection
strcpy(text, "L'utente era gia' registrato al servizio: riconnessione completata.\n"); //Re-connection completed
reg[i].status = 'c';
reg[i].address.sin_family = client.sin_family;
reg[i].address.sin_port = p;
reg[i].address.sin_addr.s_addr = client.sin_addr.s_addr;
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
if(reg[i].msg > 0) { //messaggi offline
strcpy(text, "Messaggi offline presenti.\n"); //Offline messages found
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending number of messages to be sent
length = sizeof(*last);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
l = htons(*last);
rv = send(sckt, (void*) &l, length, 0);
if(rv < 0) {};
for(j=0; j<reg[i].msg; j++) {
sprintf(buffer, "%s > ", reg[i].sender[j]);
strcat(buffer, reg[i].pendent[j]);
length = strlen(buffer);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) buffer, length, 0);
if(rv < 0) {};
};
//Set number of messages to send to 0
reg[i].msg = 0;
} else {
strcpy(text, "Nessun messaggio offline presente.\n"); //No offline messages
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
};
return 1;
};
};
//If the user is not already registered
if(*last == MAX_UTN) { //max number of users reached
strcpy(text, "Impossibile registrarsi al servizio: numero limite di utenti raggiunto.\n"); //Max number of users reached
pos = -1;
} else {
strcpy(reg[*last].username, name);
reg[*last].address.sin_family = client.sin_family;
reg[*last].address.sin_port = p;
reg[*last].address.sin_addr.s_addr = client.sin_addr.s_addr;
reg[*last].status = 'c';
reg[*last].msg = 0;
(*last)++;
pos = 1;
};
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {}
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {}
return pos;
}
void srv_quit(int sckt, struct users* reg, char* user, int num) {
int i, length, l, rv;
char text[DIM];
memset(text, 0, DIM);
strcpy(text, "Utente disconnesso.\n"); //_User disconnected
for(i=0; i<num; i++) {
if(strcmp(reg[i].username, user) == 0) { //user actually registered to the service
if(reg[i].status == 'c') {
reg[i].status = 'o';
};
memset(®[i].address, 0, sizeof(reg[i].address));
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
printf("%s disconnesso.\n", user); //<User> disconnected
return;
};
};
//if here, user is not registered to the service
strcpy(text, "L'utente non era registrato al servizio.\n"); //User not registered
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
}
void srv_send(int sckt, struct users* reg, int num, char* dest, char* source) {
int i, rv, length, l;
char text[DIM];
char addr[ADDR_DIM];
memset(text, 0, DIM);
for(i=0; i<num; i++) {
if(strcmp(reg[i].username, dest) == 0) { //User registered
if(reg[i].status == 'c') { //user connected: provides destination UDP address to the sender (it will directly send the message)
inet_ntop(AF_INET, ®[i].address.sin_addr.s_addr, addr, sizeof(addr));
sprintf(text, "Client attualmente connesso: %s %d \n", addr , ntohs(reg[i].address.sin_port));
break;
} else { //user disconnected: save offline message
//Getting the message
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) reg[i].pendent, length, 0);
if(rv < 0) {};
reg[i].pendent[reg[i].msg][length] = '[=11=]';
strcpy(reg[i].sender[reg[i].msg], source);
reg[i].msg++;
//Sending info to te sender
strcpy(text, "Messaggio salvato per l'invio offline.\n"); //Message saved offline
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
return;
};
};
};
//Here if got into the if statement
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
}
int main(int argc, char* argv[]) {
//shared (mapped) memory
struct users* registered = mmap(0, MAX_CONN*sizeof(struct users), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
//last index with user struct to be used
int* last = mmap(0, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
int sd; //socket descriptor
int conn_sd; //socket descriptor after connect()
struct sockaddr_in my_addr;
struct sockaddr_in cl_addr;
int err; //for errors
int udp_port;
char check[NAME_DIM]; //will containg the username of the client connected
socklen_t len;
int pid;
int dim_rcvd_net, dim_rcvd, dim_sent_net, dim_sent;
char buffer[DIM];
char cmd[CMD_DIM];
char name[NAME_DIM];
char port[PORT_DIM];
*last = 0;
memset(registered, 0, MAX_CONN*sizeof(struct users));
memset(check,0, NAME_DIM);
puts("Avvio del servizio...\n"); //Service starting..
if(argc != 2) {
printf("Errore: troppi argomenti. \n Utilizzare solamente un argomento."); //Please use only one argument
exit(EXIT_FAILURE);
};
//Maybe check the argument itself?
sd = socket(AF_INET, SOCK_STREAM, 0);
memset(&my_addr, 0, sizeof(my_addr));
//Socket parameters
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[1]));
my_addr.sin_addr.s_addr = INADDR_ANY;
err = bind(sd, (struct sockaddr*) &my_addr, sizeof(my_addr));
if(err < 0) {};
err = listen(sd, MAX_CONN);
if(err < 0) {};
puts("Servizio attivo.\n"); //Service activated
//Concurrent server
while(1) {
len = sizeof(my_addr);
conn_sd = accept(sd, (struct sockaddr*) &cl_addr, &len);
//error check to be done
pid = fork();
if(pid == 0) {
//Child
close(sd);
while(1) {
//Command dimension
err = recv(conn_sd, (void*) &dim_rcvd_net, sizeof(dim_rcvd), 0);
if(err < 0) {};
dim_rcvd = ntohs(dim_rcvd_net);
//Command
err = recv(conn_sd, (void*) buffer, dim_rcvd, 0);
if(err < 0) {};
buffer[dim_rcvd] = '[=11=]';
sscanf(buffer, "%s", cmd);
if(strcmp(cmd, "!help") == 0) {
srv_help(conn_sd);
};
if(strcmp(cmd, "!register") == 0) {
if(strlen(check) > 0) { //Already connected with another username
puts("Sei già connesso con altro nome.\n"); //Already connected
} else {
sscanf(buffer, "%s %s %s", cmd, name, port);
udp_port = atoi(port);
if(srv_register(conn_sd, registered, name, cl_addr, udp_port, last) == 1) {
strcpy(check, name);
};
};
};
if(strcmp(cmd, "!deregister") == 0) {
if(strlen(check) <= 0) { //if not registered
strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione.\n"); //You have to be registered
dim_sent = strlen(buffer);
dim_sent_net = htons(dim_sent);
err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
if(err < 0) {};
err = send(conn_sd, (void*) buffer, dim_sent, 0);
if(err < 0) {};
} else {
srv_deregister(conn_sd, registered, check, last);
strcpy(check, "");
};
};
if(strcmp(cmd, "!send") == 0) {
if(strlen(check) <= 0) { //if not registered
strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione.\n"); //You have to be registered
dim_sent = strlen(buffer);
dim_sent_net = htons(dim_sent);
err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
if(err < 0) {};
err = send(conn_sd, (void*) buffer, dim_sent, 0);
if(err < 0) {};
} else {
sscanf(buffer, "%s %s", cmd, name);
srv_send(conn_sd, registered, *last, name, check);
};
};
if(strcmp(cmd, "!who") == 0) {
if(strlen(check) <= 0) { //if not registered
strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione"); //You have to be registered
dim_sent = strlen(buffer);
dim_sent_net = htons(dim_sent);
err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
if(err < 0) {};
err = send(conn_sd, (void*) buffer, dim_sent, 0);
if(err < 0) {};
} else {
srv_who(conn_sd, registered, *last);
};
};
if(strcmp(cmd, "!quit") == 0) {
srv_quit(conn_sd, registered, check, *last);
close(conn_sd);
exit(EXIT_SUCCESS);
};
};
} else {
//Parent
close(conn_sd);
};
};
close(sd);
return 0;
}
编辑 2
有些东西我不明白。我刚刚重新执行了我粘贴在这里的完全相同的程序,它似乎可以正确发送消息(目前仅在线)。
可能是同步问题?
编辑 3 这是作业的文本:http://www2.ing.unipi.it/c.vallati/files/reti/Progetto2017.pdf
正如我在评论中所说,它是意大利语..
每个端口都可以用于udp或tcp用途
有一些端口被 IANA 正式分配给特定用途
像 http 80 和 ftp 21 ...(维基百科页面中的 table)
你的问题是你的接收程序读取的比客户端发送的多
因为通过套接字发送的字符串不包含任何终止字符“\0”(strlen 不计算终止字符“\0”)
所以服务器首先读取消息并且不会停止读取所以它打印那些奇怪的字符
解决方案是通过向发送函数中的 strlen(stirng) 添加一个来发送终止字符,如下所示
bytes_sent = send(client_socket, thestring, strlen(thestirng)+1, 0);
好的,我的问题解决了!
首先,我移动了UDP套接字的创建。以前,在我粘贴在这里的代码中,它在 fork()
之前。仔细想想,我认为这是错误的,因为 parent 和 child 会共享套接字,而 child 会一直使用它,看来我是对的。所以我为 parent(发送给其他客户端)和 child(接收传输)创建了一个单独的 UDP。
然后我想通了为什么代码有时有效有时无效:在我之前的执行中,当程序冻结时我用 Ctrl+Z
将其关闭;查看 htop
的输出,我注意到它的一些执行(准确地说是 children)仍然是 运行 并占用套接字。所以我继续终止它们。
在添加了更多的错误捕获代码并更正了通信中的错误之后,一切都很顺利,现在仍然如此。
这次我确保 parent kill()
关闭时 child。
是的,关于 "dual" 传输的方法(首先是文本的维度,然后是文本本身),所有工作都按预期进行。
我有一个简单的问题。 至少我觉得很简单!
我正在做一个大学项目,我已经构建了一个服务器-客户端即时消息服务,结构如下:
-服务器创建一个TCP套接字来监听客户端请求
- 除了其他功能外,每个客户端都可以向另一个客户端发送消息(甚至离线)使用 UDP
我已将端口 4242 用于服务器,然后尝试将 4243 和 4244 用于两个示例客户端。
我的问题是:这些端口是否不接受 table 用于 UDP 通信?
我问是因为当我尝试向这些端口发送消息时,接收客户端获取并打印 "strange" 字符串。
准确地说,它存储了先行消息并首先打印新消息,然后是一大块先行消息。
另一方面,如果我使用例如 4303 和 4486,它会按预期工作。
我在查看 Wikipedia 时得到了这些数字,但由于我不确定如何解释 table(所以,如果我解释正确的话)并且因为我知道维基百科可以不太靠谱,我想我可以在这里问。
附带说明一下,如果我提供的 link 不可靠,是否有任何命令或资源可以检查这些东西?
提前致谢!
编辑:
这是我的代码!之前上传不了。。我也尽量简单说明一下。
正如我在评论中提到的,我以为我收到了,但一个客户没有收到另一个客户的消息。所以一切正常,除了 !send 功能,我猜...
客户端
客户端是使用“!注册用户名”连接到服务器的用户;这个命令:
- 如果客户端是第一次连接到服务,则将客户端注册到该服务,当然将其设置为 "online"
-如果客户端已经注册到该服务,则重新连接客户端;在这种情况下,如果有一些离线消息,服务器会将其传送给客户端
连接后,客户端可以使用以下命令:
1)!help: 显示可用命令
2)!who: 显示在线人数
3)!deregister: 顾名思义,从服务器注销客户端
4)!send username:如果目的客户端在线,服务器returnsUDP目的地址和端口,客户端直接发送消息;如果目标客户端离线,服务器会存储消息,以便在目标客户端连接后将其发送给目标客户端
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#define DIM 1024
#define ADDR_DIM 16
#define PORT_DIM 5
#define CMD_DIM 12
#define NAME_DIM 21
void cl_help(int sckt) {
int rv, length, l;
char* text = "!help";
char answer[DIM];
length = strlen(text);
l = htons(length);
//Sending command dimension
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending the command itself
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
}
void cl_quit(int sckt) {
int rv, length, l;
char* text = "!quit";
char answer[DIM];
length = strlen(text);
l = htons(length);
//Sending command dimension
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending the command itself
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
}
void cl_who(int sckt) {
int rv, length, l, n_net, n, i;
char* text = "!who";
char answer[DIM];
length = strlen(text);
l = htons(length);
//Sending the dimension of the command
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending the command itself
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if( (strcmp(answer, "Nessun utente in linea") != 0)\
&& (strcmp(answer, "Devi essere connesso per poter utilizzare questa funzione") != 0) ) { //No user online; Yuo have to be registered
//Receiving number of users online
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) &n_net, length, 0);
if(rv < 0) {};
n = ntohs(n_net);
//Getting every username
for(i=0; i<n; i++) {
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
};
};
}
void cl_deregister(int sckt) {
int rv, length, l;
char* text = "!deregister";
char answer[DIM];
//Sending command dimension
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Receiving answer
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
}
void cl_register(int sckt, char* name, char* port) {
int rv, length, l, n, n_net, i;
char msg[DIM+NAME_DIM+4];
char answer[DIM];
sprintf(msg, "!register %s %s", name, port);
//Sending command
length = strlen(msg);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) msg, length, 0);
if(rv < 0) {};
//Receiving answer
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if(strcmp(answer, "L'utente era gia' registrato al servizio: riconnessione completata.\n") == 0) { //Users already registered: reconnection
//Receiving info
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if(strcmp(answer, "Messaggi offline presenti.") == 0) { //Offline messages found
//Receiving the number of messages
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) &n_net, length, 0);
if(rv < 0) {};
n = ntohs(n_net);
//Recevingi messages
for(i=0; i<n; i++) {
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) msg, length, 0);
if(rv < 0) {};
msg[length] = '[=10=]';
puts(msg);
};
};
};
}
void cl_send(int sckt, int udp_sd, char* name, char* info, char* source) {
struct sockaddr_in dest;
int rv, length, l,p;
char msg[DIM+NAME_DIM+4];
char address[ADDR_DIM];
char port[PORT_DIM];
char answer[DIM];
char *pointer;
sprintf(msg, "!send %s", name);
//Sending command
length = strlen(msg);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) msg, length, 0);
if(rv < 0) {};
//Receiving answer
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
if(strcmp(answer, "Devi essere connesso per poter utilizzare questa funzione.\n") != 0) { //You have to be registered
if(strncmp(answer, "Client attualmente connesso", 27) == 0) { //User connected right now
pointer = answer+29; //to get only the address and the port number
sscanf(pointer, "%s %s", address, port);
address[ADDR_DIM] = '[=10=]';
port[PORT_DIM] = '[=10=]';
p = ntohs(atoi(port));
memset(&dest, 0, sizeof(dest));
memset(&msg, 0, sizeof(msg));f
//Destination UDP socket
dest.sin_family = AF_INET;
dest.sin_port = htons(p);
inet_pton(AF_INET, address, &dest.sin_addr);
//Sending direct message using UDP
sprintf(msg, "%s > ", source);
strcat(msg, info);
length = strlen(msg);
l = htons(length);
//Sending message
rv = sendto(udp_sd, (void*) &l, sizeof(l), 0, (struct sockaddr*) &dest, sizeof(dest));
if(rv < 0) {};
rv = sendto(udp_sd, (void*) msg, length, 0, (struct sockaddr*) &dest, sizeof(dest));
if(rv < 0) {};
} else { //Offline transmission: transmits message to server using TCP
length = strlen(info);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) info, length, 0);
if(rv < 0) {};
//Getting info from the server
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) answer, length, 0);
if(rv < 0) {};
answer[length] = '[=10=]';
puts(answer);
};
};
}
int main(int argc, char* argv[]) {
struct sockaddr_in srv_addr;
struct sockaddr_in my_addr;
int sd, udp;
int err;
int pid;
int length, l;
char buffer[DIM];
char cmd[CMD_DIM];
char dest[NAME_DIM];
char my_name[NAME_DIM];
char in[DIM];
char to_send[DIM+NAME_DIM+4];
char part[DIM] = "";
if(argc != 5) {
//Check argument
exit(EXIT_FAILURE);
};
//TCP socket
sd = socket(AF_INET, SOCK_STREAM, 0);
//UDP socket
udp = socket(AF_INET, SOCK_DGRAM, 0);
//Pulizia
memset(&srv_addr, 0, sizeof(srv_addr));
memset(&my_addr, 0, sizeof(my_addr));
//TCP server parameters
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(atoi(argv[4]));
inet_pton(AF_INET, argv[3], &srv_addr.sin_addr);
//my UDP parameters
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET, argv[1], &my_addr.sin_addr);
//Bind for UDP socket
err = bind(udp, (struct sockaddr*) &my_addr, sizeof(my_addr));
if(err < 0) {};
//Connecting to the server
err = connect(sd, (struct sockaddr*) &srv_addr, sizeof(srv_addr));
if(err < 0) {};
//Auto-sending help command to display available operations
strcpy(cmd, "!help");
length = strlen(cmd);
l = htons(length);
err = send(sd, (void*) &l, sizeof(l), 0);
if(err < 0) {};
err = send(sd, (void*) cmd, length, 0);
if(err < 0) {};
//Receiving info from the server -> available operations
err = recv(sd, (void*) &l, sizeof(l), 0);
if(err < 0) {};
length = ntohs(l);
err = recv(sd, (void*) buffer, length, 0);
if(err < 0) {};
buffer[strlen(buffer)] = '[=10=]';
puts(buffer);
pid = fork();
if(pid == 0) {
//Child
close(sd);
while(1) {
memset(to_send, 0, sizeof(to_send));
err = recvfrom(udp, (void*) &l, sizeof(l), 0, 0, 0);
if(err < 0) {
puts("error recvfrom");
exit(EXIT_FAILURE);
};
length = ntohs(l);
err = recvfrom(udp, (void*) to_send, length, MSG_WAITALL, 0, 0);
if(err < 0) {
puts("error recvfrom");
exit(EXIT_FAILURE);
};
to_send[length] = '[=10=]';
puts(to_send);
};
} else {
//Parent
while(1) {
//Asking the user to prompt the command
memset(cmd, 0, sizeof(cmd));
memset(in, 0, sizeof(in));
memset(dest, 0, sizeof(dest));
puts("\nInserisci un comando: "); //Insert a command
fgets(in, DIM, stdin);
in[strlen(in)-1] = '[=10=]';
sscanf(in, "%s", cmd);
cmd[CMD_DIM-1] = '[=10=]';
if(strcmp(cmd, "!help") == 0) {
cl_help(sd);
continue;
};
if(strcmp(cmd, "!deregister") == 0) {
cl_deregister(sd);
};
if(strcmp(cmd, "!register") == 0) {
sscanf(in, "%s %s", cmd, dest);
cmd[CMD_DIM-1] = '[=10=]';
dest[NAME_DIM-1] = '[=10=]';
strcpy(my_name, dest);
if(strlen(dest) == 0) { //to avoid an empty username
puts("Errore: non hai inserito alcun username; riprova.\n"); //Please insert an username
} else {
cl_register(sd, dest, argv[2]);
};
continue;
};
if(strcmp(cmd, "!who") == 0) {
cl_who(sd);
continue;
};
if(strcmp(cmd, "!send") == 0) {
sscanf(in, "%s %s", cmd, dest);
cmd[CMD_DIM-1] = '[=10=]';
dest[NAME_DIM-1] = '[=10=]';
while(1) {
memset(part, 0, sizeof(part));
fgets(part, DIM-(strlen(to_send)-NAME_DIM-4), stdin);
l = strlen(part);
if((part[l-2] == '.') && (part[l-1] == '\n') && (strlen(part) == 2)) {
part[l-2] = '[=10=]';
break;
} else {
strcat(to_send, part);
};
if(strlen(to_send) >= DIM+NAME_DIM+3) {
to_send[DIM+NAME_DIM+3] = '[=10=]';
break;
};
};
strcat(to_send, "[=10=]");
cl_send(sd, udp, dest, to_send, my_name);
memset(to_send, 0, sizeof(to_send)); //to empty the buffer used to store the message sent
continue;
};
if(strcmp(cmd, "!quit") == 0) {
cl_quit(sd);
close(sd);
break;
} else{
puts("Comando non valido.\n"); //Not a valid command
};
};
};
return 0;
}
服务器
服务器响应来自客户端的请求并被构造为并发服务器。
#include <sys/mman.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#define MAX_CONN 100
#define DIM 1024
#define ADDR_DIM 16
#define CMD_DIM 12
#define NAME_DIM 20
#define MSG_DIM DIM+NAME_DIM+4
#define PORT_DIM 5
#define MAX_UTN 1024
struct users {
char username[NAME_DIM];
char status; //c = connected, o = offline
struct sockaddr_in address;
char pendent[DIM][DIM];
char sender[DIM][NAME_DIM];
int msg; //last message to be sent
};
void srv_help(int sckt) {
int rv, length, l;
char* text = "Sono disponibili i seguenti comandi: \n\
!help --> mostra l'elenco dei comandi disponibili \n\
!register username --> registra il client presso il server\n\
!deregister --> de-registra il client presso il server\n\
!who --> mostra l'elenco degli utenti disponibili\n\
!send username --> invia un messaggio ad un altro utente\n\
(messaggio subito dopo comando\n\
!quit --> disconnette il client dal server ed esce\n\
('username': max 20 caratteri)\n\
(per terminare messaggio inserisci un . da solo)\n";//info about the available commands
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
};
void srv_who(int sckt, struct users* reg, int last) {
int i, rv, length, l;
char text[DIM];
strcpy(text, "Nessun utente in linea\n");//No user online
if(last == 0) { //if no user is online
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
} else {
strcpy(text, "Utenti in linea:\n"); //Users online:
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Here I send the number of users online, to synchronize server and client
length = sizeof(last);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
l = htons(last);
rv = send(sckt, (void*) &l, length, 0);
if(rv < 0) {};
//Transmitting users
for(i=0; i<last; i++) {
if(reg[i].status == 'c') {
length = strlen(reg[i].username);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) reg[i].username, length, 0);
if(rv < 0) {};
};
};
};
};
void srv_deregister(int sckt, struct users* reg, char* name, int* last) {
int i, rv, length, l;
char text[DIM];
strcpy(text, "Deregistrazione completata.\n"); //De-registration completed
for(i=0; i<*last; i++) {
if(strcmp(reg[i].username, name) == 0) { //user found
reg[i] = reg[*last];
(*last)--;
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
return;
};
};
}
int srv_register(int sckt, struct users* reg, char* name, struct sockaddr_in client, int p, int* last) { //char*
int i, rv, length, l, j, pos;
//char pos[MSG_DIM]; // = "";
char text[DIM];
char buffer[MSG_DIM];
memset(text, 0, DIM);
strcpy(text, "Registrazione effettuata con successo.\n"); //Registration completed
for(i=0; i<*last; i++) {
if(strcmp(reg[i].username, name) == 0) { //User already registered: re-connection
strcpy(text, "L'utente era gia' registrato al servizio: riconnessione completata.\n"); //Re-connection completed
reg[i].status = 'c';
reg[i].address.sin_family = client.sin_family;
reg[i].address.sin_port = p;
reg[i].address.sin_addr.s_addr = client.sin_addr.s_addr;
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
if(reg[i].msg > 0) { //messaggi offline
strcpy(text, "Messaggi offline presenti.\n"); //Offline messages found
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
//Sending number of messages to be sent
length = sizeof(*last);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
l = htons(*last);
rv = send(sckt, (void*) &l, length, 0);
if(rv < 0) {};
for(j=0; j<reg[i].msg; j++) {
sprintf(buffer, "%s > ", reg[i].sender[j]);
strcat(buffer, reg[i].pendent[j]);
length = strlen(buffer);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) buffer, length, 0);
if(rv < 0) {};
};
//Set number of messages to send to 0
reg[i].msg = 0;
} else {
strcpy(text, "Nessun messaggio offline presente.\n"); //No offline messages
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
};
return 1;
};
};
//If the user is not already registered
if(*last == MAX_UTN) { //max number of users reached
strcpy(text, "Impossibile registrarsi al servizio: numero limite di utenti raggiunto.\n"); //Max number of users reached
pos = -1;
} else {
strcpy(reg[*last].username, name);
reg[*last].address.sin_family = client.sin_family;
reg[*last].address.sin_port = p;
reg[*last].address.sin_addr.s_addr = client.sin_addr.s_addr;
reg[*last].status = 'c';
reg[*last].msg = 0;
(*last)++;
pos = 1;
};
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {}
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {}
return pos;
}
void srv_quit(int sckt, struct users* reg, char* user, int num) {
int i, length, l, rv;
char text[DIM];
memset(text, 0, DIM);
strcpy(text, "Utente disconnesso.\n"); //_User disconnected
for(i=0; i<num; i++) {
if(strcmp(reg[i].username, user) == 0) { //user actually registered to the service
if(reg[i].status == 'c') {
reg[i].status = 'o';
};
memset(®[i].address, 0, sizeof(reg[i].address));
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
printf("%s disconnesso.\n", user); //<User> disconnected
return;
};
};
//if here, user is not registered to the service
strcpy(text, "L'utente non era registrato al servizio.\n"); //User not registered
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
}
void srv_send(int sckt, struct users* reg, int num, char* dest, char* source) {
int i, rv, length, l;
char text[DIM];
char addr[ADDR_DIM];
memset(text, 0, DIM);
for(i=0; i<num; i++) {
if(strcmp(reg[i].username, dest) == 0) { //User registered
if(reg[i].status == 'c') { //user connected: provides destination UDP address to the sender (it will directly send the message)
inet_ntop(AF_INET, ®[i].address.sin_addr.s_addr, addr, sizeof(addr));
sprintf(text, "Client attualmente connesso: %s %d \n", addr , ntohs(reg[i].address.sin_port));
break;
} else { //user disconnected: save offline message
//Getting the message
rv = recv(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
length = ntohs(l);
rv = recv(sckt, (void*) reg[i].pendent, length, 0);
if(rv < 0) {};
reg[i].pendent[reg[i].msg][length] = '[=11=]';
strcpy(reg[i].sender[reg[i].msg], source);
reg[i].msg++;
//Sending info to te sender
strcpy(text, "Messaggio salvato per l'invio offline.\n"); //Message saved offline
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
return;
};
};
};
//Here if got into the if statement
length = strlen(text);
l = htons(length);
rv = send(sckt, (void*) &l, sizeof(l), 0);
if(rv < 0) {};
rv = send(sckt, (void*) text, length, 0);
if(rv < 0) {};
}
int main(int argc, char* argv[]) {
//shared (mapped) memory
struct users* registered = mmap(0, MAX_CONN*sizeof(struct users), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
//last index with user struct to be used
int* last = mmap(0, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
int sd; //socket descriptor
int conn_sd; //socket descriptor after connect()
struct sockaddr_in my_addr;
struct sockaddr_in cl_addr;
int err; //for errors
int udp_port;
char check[NAME_DIM]; //will containg the username of the client connected
socklen_t len;
int pid;
int dim_rcvd_net, dim_rcvd, dim_sent_net, dim_sent;
char buffer[DIM];
char cmd[CMD_DIM];
char name[NAME_DIM];
char port[PORT_DIM];
*last = 0;
memset(registered, 0, MAX_CONN*sizeof(struct users));
memset(check,0, NAME_DIM);
puts("Avvio del servizio...\n"); //Service starting..
if(argc != 2) {
printf("Errore: troppi argomenti. \n Utilizzare solamente un argomento."); //Please use only one argument
exit(EXIT_FAILURE);
};
//Maybe check the argument itself?
sd = socket(AF_INET, SOCK_STREAM, 0);
memset(&my_addr, 0, sizeof(my_addr));
//Socket parameters
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[1]));
my_addr.sin_addr.s_addr = INADDR_ANY;
err = bind(sd, (struct sockaddr*) &my_addr, sizeof(my_addr));
if(err < 0) {};
err = listen(sd, MAX_CONN);
if(err < 0) {};
puts("Servizio attivo.\n"); //Service activated
//Concurrent server
while(1) {
len = sizeof(my_addr);
conn_sd = accept(sd, (struct sockaddr*) &cl_addr, &len);
//error check to be done
pid = fork();
if(pid == 0) {
//Child
close(sd);
while(1) {
//Command dimension
err = recv(conn_sd, (void*) &dim_rcvd_net, sizeof(dim_rcvd), 0);
if(err < 0) {};
dim_rcvd = ntohs(dim_rcvd_net);
//Command
err = recv(conn_sd, (void*) buffer, dim_rcvd, 0);
if(err < 0) {};
buffer[dim_rcvd] = '[=11=]';
sscanf(buffer, "%s", cmd);
if(strcmp(cmd, "!help") == 0) {
srv_help(conn_sd);
};
if(strcmp(cmd, "!register") == 0) {
if(strlen(check) > 0) { //Already connected with another username
puts("Sei già connesso con altro nome.\n"); //Already connected
} else {
sscanf(buffer, "%s %s %s", cmd, name, port);
udp_port = atoi(port);
if(srv_register(conn_sd, registered, name, cl_addr, udp_port, last) == 1) {
strcpy(check, name);
};
};
};
if(strcmp(cmd, "!deregister") == 0) {
if(strlen(check) <= 0) { //if not registered
strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione.\n"); //You have to be registered
dim_sent = strlen(buffer);
dim_sent_net = htons(dim_sent);
err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
if(err < 0) {};
err = send(conn_sd, (void*) buffer, dim_sent, 0);
if(err < 0) {};
} else {
srv_deregister(conn_sd, registered, check, last);
strcpy(check, "");
};
};
if(strcmp(cmd, "!send") == 0) {
if(strlen(check) <= 0) { //if not registered
strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione.\n"); //You have to be registered
dim_sent = strlen(buffer);
dim_sent_net = htons(dim_sent);
err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
if(err < 0) {};
err = send(conn_sd, (void*) buffer, dim_sent, 0);
if(err < 0) {};
} else {
sscanf(buffer, "%s %s", cmd, name);
srv_send(conn_sd, registered, *last, name, check);
};
};
if(strcmp(cmd, "!who") == 0) {
if(strlen(check) <= 0) { //if not registered
strcpy(buffer, "Devi essere connesso per poter utilizzare questa funzione"); //You have to be registered
dim_sent = strlen(buffer);
dim_sent_net = htons(dim_sent);
err = send(conn_sd, (void*) &dim_sent_net, sizeof(dim_sent_net), 0);
if(err < 0) {};
err = send(conn_sd, (void*) buffer, dim_sent, 0);
if(err < 0) {};
} else {
srv_who(conn_sd, registered, *last);
};
};
if(strcmp(cmd, "!quit") == 0) {
srv_quit(conn_sd, registered, check, *last);
close(conn_sd);
exit(EXIT_SUCCESS);
};
};
} else {
//Parent
close(conn_sd);
};
};
close(sd);
return 0;
}
编辑 2
有些东西我不明白。我刚刚重新执行了我粘贴在这里的完全相同的程序,它似乎可以正确发送消息(目前仅在线)。
可能是同步问题?
编辑 3 这是作业的文本:http://www2.ing.unipi.it/c.vallati/files/reti/Progetto2017.pdf
正如我在评论中所说,它是意大利语..
每个端口都可以用于udp或tcp用途 有一些端口被 IANA 正式分配给特定用途 像 http 80 和 ftp 21 ...(维基百科页面中的 table) 你的问题是你的接收程序读取的比客户端发送的多 因为通过套接字发送的字符串不包含任何终止字符“\0”(strlen 不计算终止字符“\0”) 所以服务器首先读取消息并且不会停止读取所以它打印那些奇怪的字符 解决方案是通过向发送函数中的 strlen(stirng) 添加一个来发送终止字符,如下所示
bytes_sent = send(client_socket, thestring, strlen(thestirng)+1, 0);
好的,我的问题解决了!
首先,我移动了UDP套接字的创建。以前,在我粘贴在这里的代码中,它在 fork()
之前。仔细想想,我认为这是错误的,因为 parent 和 child 会共享套接字,而 child 会一直使用它,看来我是对的。所以我为 parent(发送给其他客户端)和 child(接收传输)创建了一个单独的 UDP。
然后我想通了为什么代码有时有效有时无效:在我之前的执行中,当程序冻结时我用 Ctrl+Z
将其关闭;查看 htop
的输出,我注意到它的一些执行(准确地说是 children)仍然是 运行 并占用套接字。所以我继续终止它们。
在添加了更多的错误捕获代码并更正了通信中的错误之后,一切都很顺利,现在仍然如此。
这次我确保 parent kill()
关闭时 child。
是的,关于 "dual" 传输的方法(首先是文本的维度,然后是文本本身),所有工作都按预期进行。