我希望使用客户端服务器(具有多个客户端的服务器)完成minitalk。使用name参数启动客户端,并完成与服务器的连接。
服务器会记住每个客户端的名称。然后,在每个客户端,我可以发送消息到另一个 - 例如我有2个客户端启动 - c1和c2,我可以从c1发送消息 - c2 hello
,它应该在c2中显示为收到的消息。我也可以从c1发送到c1,这没关系。我期望的是在客户代码之下(因为服务器运行良好)
#include <stdio.h>
#include <string.h> //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h> //close
#include <arpa/inet.h> //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros
#define PORT 8080
int main(int argc, char *argv[])
{
struct sockaddr_in address;
int sock = 0, thisclient = 0, valread;
struct sockaddr_in serv_addr;
int activity;
fd_set readfds;
char *hello = malloc(100);
char tab[1024] = {0};
char* name = argv[1];
if((thisclient = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(thisclient, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
} else {
send(thisclient, name, strlen(name), 0);
}
valread = read(thisclient , tab, 1024);
if(strcmp(tab,name) != 0) {
printf("Already used user!");
return -1;
} else {
bzero(tab, sizeof(tab));
send(thisclient, "Connection", 11, 0);
valread = read(thisclient , tab, 1024);
printf("%s---\nSending message to not in a list causes server exiting\n---\n",tab);
bzero(tab, sizeof(tab));
}
while(1) {
FD_ZERO(&readfds);
FD_SET(0,&readfds); //stdin
FD_SET(thisclient,&readfds);
activity = select( thisclient+1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
for(int i=0; i<thisclient+1; ++i) {
if (FD_ISSET( i , &readfds))
{
if(i== thisclient) {
char* buffer = malloc(1025);
if ((valread = read( thisclient , buffer, 1024)) != 0) {
if(strcmp(buffer,"Server error, is closing") == 0) {
exit(0);
}
printf("Received (yours name) (message): %s\n",buffer );
bzero(buffer, sizeof(buffer));
}
free(buffer);
}
else if(i == 0) {
char* buffer = malloc(1025);
if ((valread = read( 0 , buffer, 1024)) != 0) {
if(strcmp(buffer,"Server error, is closing") == 0) {
exit(0);
}
send(thisclient, buffer, 1024, 0);
printf("Send (yours name) (message): %s\n",buffer );
bzero(buffer, sizeof(buffer));
}
free(buffer);
}
}
}
}
return 0;
}
我应该可以发送&#34; ping-pong&#34;等消息,所以c1发送到c2,c2发送到c1,c1发送两个消息到c2,c2发送一个到c1等...
此刻,连接工作正常,然后我发送第一条消息(发送方和接收方都不重要,例如c1发送到c2)。我可以从c1发送许多消息到c2,但是当我想响应时(从c2到c1的消息)没有任何反应。
我也注意到,这条消息甚至不是服务器,所以它肯定是客户的错。我仍然可以从c1发送消息到c2,但不是从内到外。什么&#34;冲洗&#34;这个状态?我从c1发送消息给c1。在此消息之后,c2中的前一个也显示在c1中(最近从c1到c1)。但我必须&#34;冲洗&#34;每当我想收到来自c2的消息时。
我应该在select()
来电添加内容吗? (例如写入的参数)。或者循环没有做好?
-EDIT服务器代码
#include <stdio.h>
#include <string.h> //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h> //close
#include <arpa/inet.h> //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros
#define TRUE 1
#define FALSE 0
#define PORT 8080
int main(int argc , char *argv[])
{
int opt = TRUE;
int master_socket , addrlen , new_socket , client_socket[30] ,
max_clients = 30 , activity, i , valread , sd;
int max_sd;
struct sockaddr_in address;
char client_name[30][100];
//set of socket descriptors
fd_set readfds;
//initialise all client_socket[] to 0 so not checked
for (i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
//create a master socket
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
//set master socket to allow multiple connections ,
//this is just a good habit, it will work without this
if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
sizeof(opt)) < 0 )
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
//type of socket created
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
//bind the socket to localhost port 8888
if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
printf("Listener on port %d \n", PORT);
//try to specify maximum of 3 pending connections for the master socket
if (listen(master_socket, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
//accept the incoming connection
addrlen = sizeof(address);
puts("Waiting for connections ...");
while(TRUE)
{
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL ,
//so wait indefinitely
activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
printf("select error");
}
//If something happened on the master socket ,
//then its an incoming connection
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket,
(struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number - used in send and receive commands
printf("New connection, ip- %s, port- %d\n" , inet_ntoa(address.sin_addr) , ntohs
(address.sin_port));
char* tempclient = malloc(100);
read(new_socket, tempclient, 100);
//add new socket to array of sockets
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
strcpy(client_name[i], tempclient);
printf("Adding to list of sockets as %s - pos %d\n",client_name[i],i);
send(new_socket, client_name[i], strlen(client_name[i]), 0);
read(new_socket, tempclient, 100);
char list[1000];
strcpy(list, "List of users\n");
for(int j=0; client_socket[j] != 0; ++j) {
strcat(list, client_name[j]);
strcat(list,"\n");
}
send(new_socket, list, strlen(list), 0);
break;
} else if(strcmp(client_name[i],tempclient) == 0) {
send(new_socket, "ERROR", 5, 0);
break;
} else if(strcmp(tempclient,"exit ") == 0) {
send(new_socket, "ERROR", 5, 0);
break;
}
}
}
//else its some IO operation on some other socket
for (i = 0; i < max_clients; i++)
{
char* buffer = malloc(1025);
char* name = malloc(100);
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the
//incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&address , \
(socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d \n" ,
inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else {
strcpy(name, buffer);
strtok(name, " ");
puts("bufor = ");
puts( buffer);
for (i = 0; i < max_clients; i++) //loop seraching for name
{
if( client_socket[i] != 0 ) { //client in array
if(strcmp(client_name[i], name) == 0) {// message name same as client name
send(client_socket[i] , buffer , 1024 , 0 );
bzero(buffer, sizeof(buffer));
bzero(name, sizeof(name));
break;
}
}
else { //no more clients
for (i = 0; i < max_clients; i++) //loop after clients
{
if( client_socket[i] != 0 ) { //send error
send(client_socket[i], "Server error, is closing", 30, 0);
} else break;
}
puts("Message error, whole communication is closing\n");
exit(0);
}
}
bzero(buffer, sizeof(buffer));
bzero(name, sizeof(name));
}
}
free(buffer);
free(name);
}
}
return 0;
}
答案 0 :(得分:1)
主要错误在于服务器中的以下循环:
//else its some IO operation on some other socket
for (i = 0; i < max_clients; i++)
{
…
//Check if it was for closing , and also read the
//incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
…
//Echo back the message that came in
else {
…
for (i = 0; i < max_clients; i++) //loop seraching for name
…
break;
…
}
…
}
内部循环中使用的循环变量i
与外部循环中使用的循环变量相同,导致尝试两次读取相同的客户端消息,从而阻塞服务器,直到该客户端重新发送内容。
第二个错误是tempclient
尽管用作字符串,但并不总是以空值结尾。快速修复:将char* tempclient = malloc(100)
替换为char *tempclient = calloc(100, 1)
。