我正在C 中开发服务器/客户端应用程序
事实上,我试图让服务器接受新的客户端,并(几乎)同时接收数据。我发送了两次数据,第一次我发送登录信息,它就工作了。
第二次我发送一些字符串数据,就像客户端一次又一次地发送它们,但我已经检查过了,它们只发送了一次。
有人能帮帮我吗?
我使用gcc-Wall-pedantic来编译它们。
这是客户端代码:需要一个参数,它可以是任何文本
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/un.h>
#include <sys/socket.h>
#define PATH "soquette"
#define BACKLOG 2
#define TAILLE_BUFFER 256
#define TIME_SLEEP 10
int main(int argc,char ** argv){
if(argc == 2){
struct sockaddr_un addr;
int serveur_socket;
ssize_t taille_lue;
char buffer[TAILLE_BUFFER];
char * buffer2;
if((serveur_socket = socket(PF_UNIX,SOCK_STREAM,0))<0){
perror("socket n");
exit(EXIT_FAILURE);
}
memset(&addr,0,sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path,PATH,sizeof(addr.sun_path)-1);
if(connect(serveur_socket,(struct sockaddr *)&addr,sizeof(struct sockaddr_un))<0){
perror("connect n");
exit(EXIT_FAILURE);
}
printf("pseudo %s n",argv[1]);
if(write(serveur_socket,argv[1],strlen(argv[1])*sizeof(char))<0){
perror("1 st write n");
exit(EXIT_FAILURE);
}
sleep(5);
taille_lue = read(STDIN_FILENO,buffer,TAILLE_BUFFER);
buffer2 = malloc(sizeof(int) + taille_lue * sizeof(char));
sprintf(buffer2,"%ld",taille_lue);
strcat(buffer2,buffer);
if(write(serveur_socket,buffer2,sizeof(buffer2))<0){
perror("write n");
exit(EXIT_FAILURE);
}
printf("message envoyé %s n",buffer2);
free(buffer2);
exit(EXIT_SUCCESS);
}
else{
printf("bad arguments number n");
exit(EXIT_SUCCESS);
}
exit(EXIT_SUCCESS);
}
这是服务器端。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <signal.h>
#include <fcntl.h>
#define PATH "soquette"
#define NB_MAX_CONNECTION 10
#define TAILLE_BUFFER 256
#define NB_BOUCLE 10
#define TIME_WAIT 10
int socket_server;
void signal_handler(){
printf("signal handler n");
if(close(socket_server)==-1){
perror("close n");
}
if(unlink(PATH)==-1){
perror("unlink n");
}
exit(EXIT_SUCCESS);
}
int main(){
int i,retval,j,fd_max,new_fd;
ssize_t taille_recue;
struct sockaddr_un addr;
char buffer[TAILLE_BUFFER];
struct timeval tv;
fd_set rfds,active_fd_set;
if(signal(SIGINT,signal_handler)==SIG_ERR){
perror("signal n");
}
tv.tv_sec=TIME_WAIT;
tv.tv_usec=0;
FD_ZERO(&rfds);
FD_ZERO(&active_fd_set);
printf("server launch n");
if((socket_server = socket(PF_UNIX,SOCK_STREAM,0))<0){
perror("socket n");
exit(EXIT_FAILURE);
}
memset(&addr,0,sizeof(struct sockaddr_un));
addr.sun_family = PF_UNIX;
strncpy(addr.sun_path,PATH,sizeof(addr.sun_path)-1);
if((bind(socket_server,(struct sockaddr *)&addr,sizeof(struct sockaddr_un))==-1)){
perror("bind n");
exit(EXIT_FAILURE);
}
if(listen(socket_server,NB_MAX_CONNECTION)==-1){
perror("listen n");
exit(EXIT_FAILURE);
}
FD_SET(socket_server,&active_fd_set);
fd_max = socket_server;
for(i=0;i<NB_BOUCLE;i++){
FD_ZERO(&rfds);
rfds = active_fd_set;
printf("tour number %d n",i);
if((retval = select(fd_max+1,&rfds,NULL,NULL,&tv))<0){
perror("select n");
}
for(j=0;j<=fd_max;j++){
if(FD_ISSET(j,&rfds)){
if(j == socket_server){
if((new_fd = accept(socket_server,NULL,NULL))<0){
perror("accept n");
signal_handler();
exit(EXIT_FAILURE);
}
printf("new client n");
FD_SET(new_fd,&active_fd_set);
if(read(new_fd,buffer,TAILLE_BUFFER)<0){
perror("read 1n");
}
else{
printf("read from buffer %s n",buffer);
fd_max = new_fd;
}
}
else{
printf("client already in the list n");
if((taille_recue = read(j,buffer,sizeof(int)))<0){
if(taille_recue == 0){
close(j);
FD_CLR(j,&rfds);
}
else{
signal_handler();
perror("read server 2 n");
exit(EXIT_FAILURE);
}
}
else{
printf("read from buffer %s n",buffer);
FD_CLR(j,&rfds);
}
}
}
}
}
printf("fermeture du serveur n");
close(socket_server);
unlink(PATH);
exit(EXIT_SUCCESS);
}
这是客户端输出
/client 1
pseudo 1
salut
message envoyé 6salut
/0
这是服务器输出
MacBook-Pro-de-Kevin:tp10 kevin$ ./server
server launch
tour number 0
new client
read from buffer 1
tour number 1
client already in the list
read from buffer 6sal
tour number 2
client already in the list
read from buffer ut
/
tour number 3
client already in the list
read from buffer ut
/
tour number 4
client already in the list
read from buffer ut
/
tour number 5
client already in the list
read from buffer ut
/
tour number 6
client already in the list
read from buffer ut
/
tour number 7
client already in the list
read from buffer ut
/
tour number 8
client already in the list
read from buffer ut
/
tour number 9
client already in the list
read from buffer ut
/
fermeture du serveur
服务器无法正确处理连接的套接字
-
首先,当服务器接受新连接时,它会立即尝试从套接字读取数据。此时可能没有可用的数据,因此读取可能会阻塞。虽然这不能解释你所问的问题,但它与你的目标相冲突。
-
服务器假设任何新接受的连接的fd都必须是集合中的最大fd。尽管它还没有影响到你,但这种假设是不安全的。文件描述符被释放,并在关闭时可供重用。
-
服务器在关闭连接时不会更新
fd_max
。然而,尽管这可能会导致后续的select()
调用不严格符合该函数的规范,但它可能不会导致任何实际的不当行为。
服务器和客户端无法正确处理I/O
-
您似乎假设客户端的
write()
调用总是向其写入指定的全部字节数,并且服务器的下一个read()
将读取最多写入的所有字节。这些假设通常是不安全的,尽管unix域套接字确实很有可能满足这些假设。通常,对于read()
和write()
,您必须考虑返回值,不仅要发现错误/文件结尾,还要确保写入/读取所有预期字节。您必须准备好循环,以便传输所有需要的字节。 -
在基于
select()
的场景中,前一点中描述的循环需要通过select()
循环,否则可能会引入阻塞。因此,您可能需要对每个连接计算在任何给定时间预计读取/写入的字节数。事实上,除非您的服务器除了尽可能快地将字节从源移动到接收器之外什么都不做,否则它很可能需要维护一些每个连接的状态。 -
奇怪的是,对于一个已建立的连接,您试图在任何给定的读取中只读取
int
中的字节数,而实际上可能有更多的字节可用,并且缓冲区可以容纳更多的字节。就在这里:
if((taille_recue = read(j,buffer,sizeof(int)))<0){
-
现在仔细考虑上面引用的行:只有当
read()
返回负值时,才会执行if
块。特别是,当read()
返回0
以指示文件结束时,该块不是执行的,但它在该块中,而不是在else
块中,用于测试文件结束条件这就是你所询问的行为的原因位于EOF的打开文件总是可以读取,但您对来自read()
的EOF信号处理不当,将其视为已读取数据,而不是识别其内容。 -
此外,如果要通过
printf()
和%s
字段描述符打印缓冲区内容,则必须确定在有效数据之后向缓冲区中插入一个空字符(' '
),或者使用将输出限制为缓冲区中有效字节数的最大字段宽度。