Linux之socket编程:编程注意事项(一)

之前讲述了Linux下的网络编程API以及地址处理等内容,这篇博客主要记录下一些细节问题。
咱们知道Linux编程,网络进程是从缓冲中读取数据,那么其缓冲是怎么样的呢,是把接收到的数据存放在一个连续的内存中,还是按照分组的到达来存放的?在Linux中,网络接收到的数据是按分组来缓冲的。每个socket结构中有三个链表,即接收链表、发送链表、异常链表。这个三个链表上会存放对应的数据,并且是以分组(包)为单位UDP在接收的时候需要注意,尽量使接受的buffer足够大,要不在默认情况下会剩下的数据被丢失。而TCP不会出现这样的情况
下面是我的实验源码,其中client是客户端,而service是服务器。

/*client.c*/
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>

int main(int argc,char*argv[])
{
	struct sockaddr_in service,client;
	int fd,ret;
	char buf[40]={0};
	socklen_t addrlen = sizeof(client);
	
	fd = socket(AF_INET,SOCK_DGRAM,0);
	if(fd < 0){perror("socket");exit(-1);}
	memset(&service,0,sizeof(service));
	service.sin_family = AF_INET;
	service.sin_port = htons(5050);
	service.sin_addr.s_addr = inet_addr("127.0.0.1");
	
	memcpy(buf,argv[1],strlen(argv[1]));
	
	while(1){
		ret = sendto(fd,buf,sizeof(buf),0,(struct sockaddr*)&service,addrlen);
		if(ret == -1){perror("sendto");close(fd);exit(-1);}
		sleep(1);
	}
	
	close(fd);
}

/*service.c*/
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>

int main(int argc,char*argv[])
{
	struct sockaddr_in service,client;
	int fd,ret;
	char buf[40];
	socklen_t addrlen = sizeof(client);
	
	fd = socket(AF_INET,SOCK_DGRAM,0);
	if(fd < 0){perror("socket");exit(-1);}
	memset(&service,0,sizeof(service));
	service.sin_family = AF_INET;
	service.sin_port = htons(5050);
	service.sin_addr.s_addr = inet_addr("127.0.0.1");
	
	ret = bind(fd, (struct sockaddr*)&service, sizeof(struct sockaddr));
	if(ret <0){perror("bind");exit(-1);}

	
	while(1){
		ret = recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&client,&addrlen);
		printf("%s string len %d\n",buf,ret);
		printf("recv ip %s port %d \n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));				
		memset(buf,0,sizeof(buf));
		sleep(2);
	}
	
	close(fd);
}

其中功能很简单,UDP中的客户端一直向服务器发送数据,并且发送的速度比接受的要快。在服务器端把接收到的数据打印出来。

  • 实验过程如下,同时运行两个客户端,分别向服务器发送“12346567890”和“abcdefg”两个字符串,并且都比服务器指定的接收buffer要小。在服务器上运行后,发现这两个字符串是单独接收到的,并没有粘在一起。
    在这里插入图片描述
    在这里插入图片描述
  • 在启动客户端时,发送的数据比服务器直接的buffer大时,服务器上接收到的数据只有buffer长度的,其余的会丢失。现象如下,发送方每次发送30个字节的报文,但是在接收时是20个一接收,发现其余的10个字节不会在下次接收中获取,而是丢失了。
    在这里插入图片描述
    在这里插入图片描述
    但是在TCP中这个是不会出现的,现象如下。
    在这里插入图片描述
    在这里插入图片描述
  • 关闭发送方后,服务器中的还能持续一段时间可以读出报文。这是因为其接收缓冲中使用的是链表,所以可以缓冲一些分组。具体可以缓冲多小,可以自己设置。
    在很多地方能看到给网络地址复制时会使用INADDR_ANY这个宏,其值为0,0在网络中代表的是this即这个网络上的本主机。所以用它来表示本网络,只有在接收端可以使用这个宏,发送的时候不能使用这个宏。因为网络中不允许目标为全0,全0找不到对方。在多网卡设备中时,服务器端会比较喜欢使用这个宏。因为假如要接收所有端口的分组,不用去挨个设置使用每个端口的地址结构信息,这样特别方便
    在这里插入图片描述
    在网络编程基础篇中讲述了网络中经常使用的地址转换函数,如inet_aton,inet_addr,inet_network,inet_ntoa等等,这些函数都可以把字符串类型的点分十进制和二进制的IP数据(有些还会带网络序转换)之间的转换。但是没有函数可以把二进制的IP指针转换为点分十进制的字符串类型。这里要补充一个函数inet_ntop,其和inet_ntoa功能是一致的,只不二进制IP参数的形式不一样。
       #include <arpa/inet.h>
       const char *inet_ntop(int af, const void *src,
                             char *dst, socklen_t size);
		/*af 指的是使用的IP版本,可以是AF_INET和AF_INET6,而srx则是二进制的IP地址指针,dst为点分十进制的IP地地,size表示的是dst的长度(字节数)*/

下面是使用gethostbyname函数来模拟dns的功能。根据用户输入的主机名,会获取相应的信息,如IP地址,IP的版本等。需要注意的是使用gethostbyname函数获取的主机信息,其中IP地址列表中保存的IP地址是网络序,在使用中需要自己装换。

#include<stdio.h>
#include<netdb.h>
#include<arpa/inet.h>

int main(int argc,char *argv[])
{
    if(argc < 2){printf("please enter host name\n");return -1;}
    struct hostent *host = NULL;
    int i =0;
    char ip[30];
    host = gethostbyname(argv[1]);
    if(host == NULL){perror("gethostbyname");return -1;}
    printf("host name %s \n ",host->h_name);
    i = 0;
    while(host->h_aliases[i]){
	printf("aliases[%d]:%s \n",i+1,host->h_aliases[i]);
	i++;	
   }	
   printf("host addr type %s \n",host->h_addrtype==AF_INET?"AF_INET":"AF_INET6");
   printf("host addr length %d \n",host->h_length);
   i = 0;
   while(host->h_addr_list[i]){/*这里的IP地址列表为网络序,如果不使用这个函数,自己需要转换*/
	printf("host address[%d]: %s\n",i+1,inet_ntop(AF_INET,host->h_addr_list[i],ip,sizeof(ip)));
	i++;		
   }
}
发布了35 篇原创文章 · 获赞 1 · 访问量 1870

猜你喜欢

转载自blog.csdn.net/lzj_linux188/article/details/105298429