嵌入式linux的网络编程(3)--TCPClient程序设计
嵌入式linux的网络编程(3)--TCP Client程序设计
1.概述
客户端主要需要完成与服务器建立连接,请求数据,应答数据等工作.从代码上来看,客户端程序有很多代码与服务器端程序类似,我们先给出一个简单的客户端源码,随后进行详细的讲解.
1 /**************************************************************************************/ 2 /*简介:TCPClient示例。 */ 3 /*************************************************************************************/ 4 #include <stdlib.h> 5 #include <stdio.h> 6 #include <errno.h> 7 #include <string.h> 8 #include <netdb.h> 9 #include <sys/types.h> 10 #include <netinet/in.h> 11 #include <sys/socket.h> 12 13 int main(int argc, char *argv[]) 14 { 15 int sockfd; 16 char buffer[1024]; 17 struct sockaddr_in server_addr; 18 struct hostent *host; 19 int portnumber,nbytes; 20 21 if(argc!=3) 22 { 23 printf("Usage:%s hostname portnumber/a/n",argv[0]); 24 exit(1); 25 } 26 27 if((host=gethostbyname(argv[1]))==NULL) 28 { 29 herror ("Get host name error/n"); 30 exit(1); 31 } 32 33 if((portnumber=atoi(argv[2]))<0) 34 { 35 printf("Usage:%s hostname portnumber/a/n",argv[0]); 36 exit(1); 37 } 38 39 /* 客户程序开始建立 sockfd描述符 */ 40 if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) 41 { 42 printf("Socket Error:%s/a/n",strerror(errno)); 43 exit(1); 44 } 45 /* 客户程序填充服务端的资料 */ 46 bzero(&server_addr,sizeof(server_addr)); 47 server_addr.sin_family=AF_INET; 48 server_addr.sin_port=htons(portnumber); 49 server_addr.sin_addr=*((struct in_addr *)host->h_addr); 50 /* 客户程序发起连接请求 */ 51 if(connect(sockfd,(struct sockaddr *)(&server_addr),/ 52 sizeof(struct sockaddr))==-1) 53 { 54 printf("Connect Error:%s(%d)/a/n",strerror(errno),errno); 55 exit(1); 56 } 57 /* 连接成功了 */ 58 if((nbytes=read(sockfd,buffer,1024))==-1) 59 { 60 printf("Read Error:%s/n",strerror(errno)); 61 exit(1); 62 } 63 buffer[nbytes]='/0'; 64 printf("I have received:%s/n",buffer); 65 66 /* 结束通讯 */ 67 close(sockfd); 68 exit(0); 69 }客户端程序首先连接到服务器端,然后才能进行数据交换,在这之前就必须知道服务器的IP地址和端口号.
2.名字地址转化
通常,人们在使用过程中都不愿意记忆冗长的IP 地址,尤其到IPv6时,地址长度多达128位,那时就更加不可能一次次记忆那么长的IP地址了.因此,使用主机名将会是很好的选择.在Linux中,同样有一些函数可以实现主机名和地址的转化,最为常见的有gethostbyname,gethostbyaddr,getaddrinfo等,它们都可以实现IPv4和IPv6的地址和主机名之间的转化.其中gethostbyname是将主机名转化为IP地址,gethostbyaddr则是逆操作,是将IP地址转化为主机名,另外getaddrinfo还能实现自动识别IPv4地址和IPv6地址.
struct hostent{ char *h_name; /*正式主机名*/ char **h_aliases; /*主机别名*/ int h_addrtype; /*地址类型*/ int h_length; /*地址长度*/ char **h_addr_list; /*指向IPv4或IPv6的地址指针数组*/}调用该函数后就能返回hostent结构体的相关信息.getaddrinfo函数涉及到一个addrinfo的结构体,如下所示:
struct addrinfo { int ai_flags; /*AI_PASSIVE,AI_CANONNAME;*/ int ai_family; /*地址族*/ int ai_socktype; /*socket类型*/ int ai_protocol; /*协议类型*/ size_t ai_addrlen; /*地址长度*/ char *ai_canoname; /*主机名*/ struct sockaddr *ai_addr;/*socket结构体*/ struct addrinfo *ai_next;/*下一个指针链表*/ }
对hostent结构体而言,addrinfo结构体包含更多的信息.
如果该函数发生错误时,但全局变量errno中不存储错误代码,h_errno中存储的才是错误代码.通过herrno函数访问变量h_errno,该函数的使用与perror的用法一样,如果例子中的29行.
此外,还可以通过gethostname函数获得本地主机的名字,返回的名字可以用于gethostbyname,该函数的声明如下:
#include <unistd.h>int gethostname(char *hostname, size_t size);hostname: 一个指向将要存放主机名的缓冲区指针;
getaddrinfo函数的语法要点如下:
在调用之前,首先要对hints服务线索进行设置.它是一个addrinfo 结构体,下图列举了该结构体常见的选项值.
3.连接服务器
当客户端完成创建socket,填充服务器信息结构等工作后,就可以连接服务器了,如例子中的第51行.connect函数的语法要点如下:
当connect函数出错时,全局变量errno含有下面的值:
EACCES,EPERM 用户试图在套接字广播标志没有设置的情况下连接广播地址或由于防火墙策略导致连接失败.
当connect函数成功返回后,就可以使用sockfd作为与服务器连接的套接字描述符,一些之前提到的I/O函数,都可以用来与服务器进行通信了.
4.测试结果
将这两个程序分别编译为simple_server和simple_client,通过SecureCRT建立两个和linux操作系统的连接,分别运行这两个程序.
注意:localhost是本地循环地址,它代表本机的IP地址,用点分十进制表示为127.0.0.1.这是一个特殊的IP地址,通常用来测试IP协议是否正常.在本地调试网络程序时会经常用到这个地址.
- 11-131分钟学会U盘启动安装Linux系统
- 11-13克隆MAC地址来绕过强制门户
- 11-13Linux运维常见故障及处理的 32 个锦囊妙计
- 11-13如何快速以管理员权限运行Linux命令?
- 11-13超全面的Linux应急响应技巧
- 11-136 款面向 Linux 用户的开源绘图应用程序
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 12-25优酷推出U镜到底等直播功能 已应用在羽毛球
- 12-25百川智能正式发布全链路领域增强大模型
- 12-25SHEIN4家仓储物流园获“零废工厂”认证
- 12-25西方博主在TikTok上展现中国风貌,“China
- 12-05亚马逊推出新一代基础模型 任意模态生成大模