daytimetcpcli.c
#include "unp.h" #include "my_err.h" int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) err_quit("usage: a.out <IPaddress>"); if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(13); /* daytime server */ if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) err_quit("inet_pton error for %s", argv[1]); if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) err_sys("connect error"); while ( (n = read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = 0; /* null terminate */ if (fputs(recvline, stdout) == EOF) err_sys("fputs error"); } if (n < 0) err_sys("read error"); exit(0); }
创建TCP套接字
使用函数,参考链接:
[dm href=’http://baike.baidu.com/link?url=sGKti6WLouLRLE3481kH_uf5DUcmNCOnjWBIBe3I08OzUHKu3o35fioN9CeybOonnxdEgTTyu-jW7WhKpMUTUK’]百度百科参考[/dm]
int socket(int domain, int type, int protocol);
其中domain表示何种地址类型
type参数的作用是设置通信的协议类型
参数protocol用来指定socket所使用的传输协议编号。这一参数通常不具体设置,一般设置为0即可
网际套接字地址结构
在《unix网络编程书中》把服务器IP地址和端口号填入一个网络套接字地址结构
即:一个名为servaddr的sockaddr_in结构变量
/*IPv4地址结构*/ struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family;//地址族 in_port_t sin_port;//端口号 16位 两个字节 struct in_addr sin_addr;//地址 32位 四个字节 char sin_zero[8];//保留字段,一般不使用,一般将其设置为0 }; struct in_addr { unit32_t s_addr; //地址应该为网络字节序 }; sin_len:结构体大小,有一些平台下没有这个字段 sin_family:指定该地址家族(地址族),在IPv4套接字地址结构中,必须为AF_INET //socket不仅能用与TCP/IP协议,还可以用于Unix域协议 AF_INET6 为IPv6协议 sin_addr:IPv4的地址,结构体,结构体中只有一个成员,成员为无符号的32位整数 sin_port:无符号的16位整数,最大值是65535
在上述地址结构中,只需要注意地址族,端口号,地址即可。
通用地址结构:
struct sockaddr { unit8_t sin_len; sa_family_t sin_family; char sa_data[14];//14个字节 } sin_len:整个sockaddr结构体的长度 sin_family:指定该地址家族 sa_data:由sin_family决定它的形式
有可能不在TCP/IP协议中,所以一般使用通用地址,
使用顺序是填充第一类地址,再将第一类地址强制转换为第二类地址
比如:
struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); //servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //连接服务器 if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr))<0) ERR_EXIT("connect");
memset和bzero
在使用地址结构之前需要将地址结构清零,至于为什么需要清零我还不是很了解
如果各位读者有所了解,欢迎评论。
memset函数:
void *memset(void *s, int ch, size_t n);
memset的大致含义为将s的前n个字节填充为ch
如上述定义地址结构中使用的memset函数。
memset(&servaddr, 0, sizeof(servaddr));
bzero函数:
置字节字符串前n个字节为零且包括‘\0’
如《unix网络编程》中使用的:
bzero(&servaddr, sizeof(servaddr));
字节序
大端字节序:内存地址增长的方向->将高位的数据存放在低内存地址处
小端字节序:将高位的数据存放在高内存地址处
其中:网络字节序为大端字节序
相关函数:
/*与字节序相关的转换函数*/ unit32_t htonl(unit32_t hostlong); //由4个字节的整数,有主机字节序转换为网络字节序 unit16_t htons(unit16_t hostshort);//由2个字节的整数,有主机字节序转换为网络字节序 unit32_t ntohl(unit32_t netlong); unit16_t ntohs(unit16_t netshort);
在上述函数中,h代表host, n代表network, s代表short, l代表long
地址转换函数:
/*地址转换函数*/ #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp); //将点分十进制的ip地址转换成网络地址 int_addr_t inet_addr(const char *cp); //将点分十进制的ip地址转换成32位的整数 char *inet_ntoa(struct in_addr in); //将网络字节序的地址结构转换成点分十进制的ip地址