我在C中为TCP服务器写了一些代码,它回应了它所获得的任何东西。问题是当我第一次发送数据时它回应它,下次服务器发送回我发送的第一个数据包。日志看起来像:
Client Send : Packet1
Server reply : Packet1
Client Send : Packet2
server reply : Packet1
服务器代码如下:
int main(int argc, char** argv) {
int listenfd,connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in servaddr,cliaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
printf("Socket listenfd : %d with %d And %d\n",listenfd,AF_INET,SOCK_STREAM);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
printf("Server address: %d\n",servaddr.sin_addr.s_addr);
bind(listenfd, (SA*) &servaddr, sizeof(servaddr));
printf("Listened: %d\n",listenfd);
listen(listenfd,LISTENQ);
printf("After Listening: %d\n",listenfd);
int num=0;
for( ; ; ){
clilen=sizeof(cliaddr);
connfd = accept(listenfd, (SA*) &cliaddr,&clilen);
printf("Client no. %d connected\n",++num);
if( (childpid=fork())==0){
close(listenfd);
echo(connfd);
exit(0);
printf("Client no. %d Terminated\n",++num);
}
close(connfd);
}
return (EXIT_SUCCESS);
}
我的回声功能:
void echo(int sockfd) {
ssize_t n;
char buf[MAXLINE];
again:
while ( (n = read(sockfd, buf, MAXLINE)) > 0)
writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
printf("read error");
}
客户端代码main:
int main(int argc, char** argv) {
int sockfd;
struct sockaddr_in servaddr;
sockfd= socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port= htons(SERV_PORT);
inet_pton(AF_INET,"0.0.0.0",&servaddr.sin_addr);
printf("%d , %d \n",sockfd,servaddr.sin_addr.s_addr);
connect(sockfd, (SA*) &servaddr,sizeof(servaddr));
printf("%d\n",sockfd);
replyBack(stdin,sockfd);
printf("RETURN\n");
return (EXIT_SUCCESS);
}
replyBack函数:
void replyBack(FILE *fp, int sockfd) {
char sendline[MAXLINE], recvline[MAXLINE];
printf("ENTER YOUR ECHOED: \n");
while (fgets(sendline, MAXLINE, stdin) != NULL) {
write(sockfd, sendline, sizeof(sendline));
if (read(sockfd, recvline, MAXLINE) == 0)
{
printf("str_cli: server terminated prematurely");
exit(-1);
}
fputs(recvline, stdout);
}
}
答案 0 :(得分:4)
好吧,让我们看一下代码的这一部分:
printf("After Listening: %d\n",listenfd);
int num=0;
for( ; ; ){
clilen=sizeof(cliaddr);
connfd = accept(listenfd, (SA*) &cliaddr,&clilen);
printf("Client no. %d connected\n",++num);
if( (childpid=fork())==0){
close(listenfd);
echo(connfd);
exit(0);
printf("Client no. %d Terminated\n",++num);
}
close(connfd);
}
调用exit
会退出您的应用,因此跟随它的printf
永远不会被执行。轻微,但值得指出。
此外,在“子”进程中,您不应关闭侦听套接字。它应该使用的唯一套接字是客户端连接,所以你应该有更多的东西:
if ( (childpid = fork ()) == 0 ) {
echo ( connfd );
close ( connfd );
printf ( "Client no %d terminated.\n", num ); /* Don't use the ++ here or your count will be off */
exit ( 0 );
}
现在让我们来看看你的回音代码:
void echo(int sockfd) {
ssize_t n;
char buf[MAXLINE];
again:
while ( (n = read(sockfd, buf, MAXLINE)) > 0)
writen(sockfd, buf, n);
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
printf("read error");
}
必须记住,对read
和write
的调用可能会阻止(因为我没有看到您将套接字设置为非阻塞IO),并且write
可能无法发送调用时整个缓冲区,所以你需要在这里查看更多的东西。
void
echo ( int sockfd )
{
ssize_t bytes_in, bytes_out, bytes_remaining;
int write_err;
char buf[MAXLINE];
char * send_start_pos;
while ( 1 ) {
bytes_in = read ( sockfd, buf, MAXLINE );
if ( bytes_in < 1 ) {
if ( errno == EINTR )
continue;
break; /* other error occurred, or EOF (0 bytes read) */
}
bytes_remaining = bytes_in;
send_start_pos = buf;
write_err = 0;
while ( ( bytes_remaining > 0 ) && !( write_err ) ) {
bytes_out = write ( sockfd, send_start_pos, bytes_remaining );
if ( bytes_out < 0 ) {
if ( errno == EINTR )
continue;
write_err = 1;
break;
}
bytes_remaining -= bytes_out;
send_start_pos += bytes_out;
}
if ( write_err )
break;
}
}
一旦您的echo
函数退出,套接字将在调用函数中关闭。一般来说,我建议关闭echo
函数中的套接字,除非之后需要它。我几乎肯定会建议在发生错误时关闭它,但是,这又取决于你。
顺便一提,远离goto
......它有其目的,但在大多数情况下,编写良好的代码很少使用它。
答案 1 :(得分:1)
最大的问题是你将TCP视为数据报协议,而不是数据报协议。这是一个流协议。
我还没有指出究竟发生了什么,但是现在我的钱在客户端打印它已经在缓冲区中的消息(而不是第二次获取它)。向我们展示客户代码。
编辑有很多错误。
首先,对write
的一次调用可能需要在另一端多次调用read
。
其次,由于TCP是一种流协议,因此您需要确保接收方知道每个逻辑消息需要多少字节。您需要坚持使用固定长度的消息,或者为每条消息添加长度前缀。你总是通过发送MAXLINE
个字节来做前者,但不够一致(例如,writen(sockfd, buf, n)
写回n
个字节,而n
可能不同来自MAXLINE
)。
另一个编辑,用于解决评论中的观点。 Telnet
是一个不好的比喻:在telnet
中,有一个流字符以单向流动,而流字符以另一种方式流动。在您的协议中,您正在发送离散的多字节消息(或 datagrams )。这正是您发现数据报协议(UDP)更容易应用于您的问题的原因。