我刚刚开始学习 Unix C 编程,有一个问题我无法解决。在这个程序中,我制作了一个带有套接字和选择功能的简单服务器。当我使用不同的终端启动尝试连接服务器的客户端程序时,服务器可以接受新的客户端并成功连接(根据我的代码打印一句话)。但是,当我尝试在客户端程序中将客户端程序中的消息发送到服务器(通过 scanf() 和 send() )时,服务器只是阻塞在那里并且无法接收到消息。 您能帮我找出程序中的缺陷吗?非常感谢您的任何建议! 这是我的服务器程序的代码(我认为客户端程序没有问题):
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <string.h>
8 #include <fcntl.h>
9 #include <sys/select.h>
10 //all kinds of header
11 main(){
12 int sfd; //server's file descriptor
13 int fdall[100]; //array for client descriptor
14 int count=0; //total number of clients
15 int maxfd=0; // max value of all descriptors
16 char buf[1024]={0}; //used for receiving message from client, by recv()
17 fd_set fds; // readset in select()
18 sfd=socket(AF_INET,SOCK_STREAM,0);
19
20 struct sockaddr_in add;
21 add.sin_family=AF_INET;
22 add.sin_port=htons(9999);
23 add.sin_addr.s_addr=inet_addr("192.168.122.1");
24 int i,j,r;
25
26 r=bind(sfd,(struct sockaddr*)&add,sizeof(add));
27 if(r==-1) printf("bind:%m\n"),exit(-1);
28 else puts("bind ok!");
29 listen(sfd,10);
30
31 while(1){
32 FD_ZERO(&fds);
33 maxfd=0;
34 FD_SET(sfd,&fds);
35 maxfd=maxfd>sfd?maxfd:sfd;
36 for(i=0;i<100;i++){
37 fdall[i]=-1;
38 }
39 r=select(maxfd+1,&fds,0,0,0);
40 if(FD_ISSET(sfd,&fds)){
41 fdall[count]=accept(sfd,0,0);
42 puts("new client!");
43 count++;
44 }
45 for(i=0;i<count;i++){
46 if(FD_ISSET(fdall[i],&fds)!=-1&&fdall[i]!=-1){
47 r=recv(fdall[i],buf,1023,0);
48 for(j=0;j<count;j++){
49 if(fdall[j]!=-1){
50 send(fdall[j],buf,r,0);
51 }
52 }
53 }
54 }
55 }
56 }
您不能使用
scanf
从套接字读取。 scanf
将从STDIN
读取;这就是导致你受阻的原因。您也不能使用 fscanf
,因为这将需要 FILE *
,尽管可以使用 freopen
从套接字创建文件,但它会假设它可以坐在那里并且 read
尽其所能,而不是使用 select
。
您需要做的是
read
从套接字到缓冲区。当您拥有适当数量的数据(可能由换行符分隔)时,请使用 sscanf
(注意额外的 s
),确保您正在解析的字符串是 NUL
终止的。
您当前的代码存在几个问题:
1.) 您的
fds
组中始终只有一组插座。这是您正在侦听新连接的套接字。您可能也想将所有连接到客户端的套接字放在那里。
2.) 测试
FD_ISSET(fdall[i],&fds)!=-1
没有任何意义。 FD_ISSET(fdall[i],&fds) 类似于一个布尔值,仅测试是否等于零。
3.) 您的测试
FD_ISSET(fdall[i],&fds)
始终结果为零,因为您已清除 sfd
中的所有值(fds
除外)。请注意,select
永远不会将文件描述符添加到集合中。它会删除那些会阻塞下一个 I/O 操作的内容。
4.) 在大循环的每次传递中,您将
fdall[i]
设置为 -1
,尽管之前的值可能是一个打开的套接字,并且您从未关闭该套接字。
在我看来,你需要重新考虑基本功能。您应该在大循环之前将
fdall[i]
设置为-1
。在循环中,您应添加从 fdall[i]
到 fds
的所有套接字。当新客户端连接时,您应跳过下一个循环迭代,以避免使用此新套接字测试 FD_ISSET(这可能超出调用 select 的值maxfd
)。希望有帮助。