Linux下Socket接收数据长度为0分析

客户端和服务端已经通过socket建立起了TCP连接,接着可以进行数据传输,这里主要用到send和recv,比较简单,他们和系统调用read和write参数类似

#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);

他们的前三个参数和write,read一样的意思,具体地说是send和write一样,recv和read一样,但是多了一个flags参数,这个标志含义可以baidu,google搜索,一般默认为0

send成功返回的是发送的字节数,出错返回-1,但是成功返回并不代表另一端已经接收数据了,仅仅说明已经发送到网络上了;recv成功返回接收消息字节数,出错返回-1,若没可用消息或者对方已经结束则返回0

于是在前面建立了TCP连接的基础上,添加数据传输的代码,在此过程当中有一些小的操作,比如监听的端口防火墙要开放等等,毕竟这是网络间传输

client端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 9999
#define IPADDR "192.168.1.3"
#define MAXSIZE 4096

int main(int argc, char **argv){
    int client_sockfd;
    struct sockaddr_in server_address;
    char eachline[MAXSIZE];
    if ((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        printf("create socket failed\n");
        exit(1);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(PORT);
    if (inet_pton(AF_INET, IPADDR, &server_address.sin_addr) == -1){
        printf("inet_pton failed!\n");
        exit(1);
    }

    if (connect(client_sockfd, (struct sockaddr *)&server_address, sizeof(server_address)) == -1){
        printf("connect failed!\n");
        exit(1);
    }
    fgets(eachline, MAXSIZE, stdin);
    if (send(client_sockfd, eachline, strlen(eachline), 0) == -1){
        printf("send data failed!\n");
        exit(1);
    }
    printf("send sucessfully!\n");
    close(client_sockfd);
    return 0;
}

server端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 9999
#define MAXSIZE 4096

int main(){
    int server_sockfd, connect_sockfd;
    struct sockaddr_in server_address;
    char buffer[MAXSIZE];
    int recv_length;

    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        printf("Create socket failed!\n");
	exit(1);
    }
    memset(&server_address, 0, sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(PORT);

    if (bind(server_sockfd, (struct sockaddr *)&server_address, sizeof(server_address)) == -1){
        printf("bind failed!\n");
	exit(1);
    }
    
    if (listen(server_sockfd, 10) == -1){
        printf("listen socket failed!\n");
	exit(1);
    }
    if ((connect_sockfd = accept(server_sockfd, (struct sockaddr *)NULL, NULL)) == -1){
        printf("connect failed!\n");
	exit(1);
    }
    printf("Connect sucessfully!\n");
    memset(buffer, 0, MAXSIZE);
    if (recv_length = recv(connect_sockfd, buffer, MAXSIZE, 0) == -1){
        printf("recv failed!\n");
	exit(1);
    }
    buffer[recv_length] = '\0';
    printf("Receive data: %s\n", buffer);
    close(connect_sockfd);
    close(server_sockfd);
    return 0;
}

分别进行编译,运行:

server端:
[lihui@localhost ~]$ cc -o server server.c
[lihui@localhost ~]$ ./server
光标

client端:
[lihui@localhost ~]$ cc -o client client.c
[lihui@localhost ~]$ ./client
光标

server端:
[lihui@localhost ~]$ ./server
Connect sucessfully!
光标

client端:
[lihui@localhost ~]$ ./client
helloworld
send sucessfully!

server端:
[lihui@localhost ~]$ ./server
Connect sucessfully!
Receive data:
[lihui@localhost ~]$

结果居然收到的数据为空

1:直接抓包分析

tcpdump port 9999 -w 9.pcap
tshark -r 9.pcap -V > 9
在查看了数据包详细信息后发现,第四个包里的确携带了传输的数据
346         [Bytes in flight: 11]
347 Data (11 bytes)
348 
349 0000  68 65 6c 6c 6f 77 6f 72 6c 64 0a                  helloworld.
350     Data: 68656c6c6f776f726c640a
351     [Length: 11]
352 
353 Frame 5: 60 bytes on wire (480 bits), 60 bytes captured (480 bits)

可见发送端应该是没有问题的,顺便看下第四个包

  1   0.000000 192.168.1.1 -> 192.168.1.3 TCP 66 62579 > distinct [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 WS=4096
  2   0.000041 192.168.1.3 -> 192.168.1.1 TCP 66 distinct > 62579 [SYN, ACK] Seq=0 Ack=1 Win=64240 Len=0 MSS=1460 SACK_PERM=1 WS=2048
  3   0.000121 192.168.1.1 -> 192.168.1.3 TCP 60 62579 > distinct [ACK] Seq=1 Ack=1 Win=65536 Len=0
  4   8.894099 192.168.1.1 -> 192.168.1.3 TCP 65 62579 > distinct [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=11
  5   8.894122 192.168.1.1 -> 192.168.1.3 TCP 60 62579 > distinct [FIN, ACK] Seq=12 Ack=1 Win=65536 Len=0
  6   8.894147 192.168.1.3 -> 192.168.1.1 TCP 54 distinct > 62579 [ACK] Seq=1 Ack=12 Win=65536 Len=0
  7   8.894201 192.168.1.3 -> 192.168.1.1 TCP 54 distinct > 62579 [FIN, ACK] Seq=1 Ack=13 Win=65536 Len=0
  8   8.894269 192.168.1.1 -> 192.168.1.3 TCP 60 62579 > distinct [ACK] Seq=13 Ack=2 Win=65536 Len=0

的确是客户端到服务器的包,前面三次握手,后面四次结束

2:gdb直接调试

41	    if (recv_length = recv(connect_sockfd, buffer, MAXSIZE, 0) == -1){
(gdb) n
45	    buffer[recv_length] = '\0';
(gdb) p recv_length 
$1 = 0
(gdb) p buffer
$2 = "helloworld\n", '\000' <repeats 4084 times>

这玩意就神奇了,居然返回是0,说实话我还又把recv研究了一遍,以及第四个参数的含义等等,依旧没找到原因

3:正当灰头土脸的时候,细看了一眼折磨我的recv,以及它的四周,就感觉这括号对不对呀?此刻心里顿时那几千头啥:

if (recv_length = recv(connect_sockfd, buffer, MAXSIZE, 0) == -1)

recv返回字节数,== -1为假,于是0赋给recv_length,所以返回字节数才是0,应该改成

if ((recv_length = recv(connect_sockfd, buffer, MAXSIZE, 0)) == -1){

重新编译,执行一把,这才对嘛~!

server端:
[lihui@localhost ~]$ cc -o server server.c 
[lihui@localhost ~]$ ./server 
光标

client端:
[lihui@localhost ~]$ cc -o client client.c
[lihui@localhost ~]$ ./client 
光标

server端:
[lihui@localhost ~]$ ./server 
Connect sucessfully!
光标

client端:
[lihui@localhost ~]$ ./client 
helloworld
send sucessfully!

server端:
[lihui@localhost ~]$ ./server 
Connect sucessfully!
Receive data: helloworld

[lihui@localhost ~]$ 

发表回复