基础知识:
struct sockaddr_in
{
sa_family_t sin_family; //地址族(Address Family)
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用
}
sa_family_t包括:
(1)AF_INET,IPv4网络协议使用的地址族
(2)AF_INET6,IPv6网络协议使用的地址族
(3)AF_LOCAL,本地通信中采用的UNIX协议的地址族
...etc...
struct in_addr{
in_addr_t s_addr; //32位IPV4地址
}
in_addr_t:IP地址,声明为uint32_t
struct sockaddr
{
sa_family_t sin_family; //地址族(Address Family)
char sa_data[14]; //地址信息
}
14=2+4+8字节
sin_port,sin_addr均以网络字节序保存。
在0x20号开始的地址中保存4字节int型数0x12345678.
大端序:高位字节放在低位地址。
0x20:0x12
0x21:0x34
0x22:0x56
0x23:0x78
小端序:高位字节放在高位地址。
0x20:0x78
0x21:0x56
0x22:0x34
0x23:0x12
目前主流的Intel系列CPU以小端序方式保存数据。
网络字节序:大端序。
htons
ntohs
htonl
ntohl
h:host:主机
n:network:网络
s:short:linux中是2个字节
l:long:linux中是4个字节
字节序相关代码:
主机字节序转网络字节序:
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc,char *argv[])
{
unsigned short host_port=0x1234;
unsigned short net_port;
unsigned long host_addr=0x12345678;
unsigned long net_addr;
net_port=htons(host_port);
net_addr=htonl(host_addr);
printf("Host ordered port:%#x \n",host_port);
printf("Network ordered port:%#x \n",net_port);
printf("Host ordered address:%#lx \n",host_addr);
printf("Network ordered address:%#lx \n",net_addr);
return 0;
}
结果:
函数:
点分10进制转32位大端序整型数
in_addr_t inet_addr(const char *string);
成功时返回32位大端序整数型值,失败时返回INADDR_NONE;
样例:
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
char *addr1="1.2.3.4";
char *addr2="1.2.3.256";
unsigned long conv_addr=inet_addr(addr1);
if(conv_addr ==INADDR_NONE)
printf("Error occured!\n");
else
printf("Network ordered integer addr: %#lx \n",conv_addr);
conv_addr=inet_addr(addr2);
if(conv_addr ==INADDR_NONE)
printf("Error occured!\n");
else
printf("Network ordered integer addr: %#lx \n",conv_addr);
return 0;
}
结果:
函数:
点分10进制字符串转32位大端序整型数,存储在struct in_addr中
int inet_aton(const char * string,struct in_addr * addr);
成功时返回1,失败时返回0.
样例:
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);
int main(int argc ,char *argv[])
{
char *addr="127.232.124.79";
struct sockaddr_in addr_inet;
if(!inet_aton(addr,&addr_inet.sin_addr))
error_handling("Conversion error");
else
printf("Network ordered integer addr: %#x \n",addr_inet.sin_addr.s_addr);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
结果:
Linux下:
迭代服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char * message);
int main(int argc,char * argv[])
{
int serv_sock,clnt_sock;
char message[BUF_SIZE];
int str_len,i;
struct sockaddr_in serv_addr,clnt_addr;
socklen_t clnt_adr_sz;
if(argc!=2)
{
printf("Usage: %s <port>\n",argv[0]);
exit(1);
}
serv_sock=socket(PF_INET,SOCK_STREAM,0);
if(serv_sock==-1)
error_handling("socker() error");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
error_handling("bind() error");
if(listen(serv_sock,5)==-1)
error_handling("listen() error");
clnt_adr_sz=sizeof(clnt_addr);
for(int i=0;i<5;i++)
{
clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_adr_sz);
if(clnt_sock==-1)
error_handling("accept() error");
else
printf("Connected client %d\n",i+1);
while((str_len=read(clnt_sock,message,BUF_SIZE))!=0)
write(clnt_sock,message,str_len);
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
回声客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char * message);
int main(int argc,char *argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[BUF_SIZE];
int str_len;
if(argc!=3)
{
printf("Usage : %s <IP> <port>\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_STREAM,0);
if(sock == -1)
error_handling("socket() error");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
if(connect(sock,(struct sockaddr *)&serv_addr,sizeof(serv_addr))==-1)
error_handling("connect() error!");
else
puts("Connected......");
while(1)
{
fputs("Input message(Q to quit):",stdout);
fgets(message,BUF_SIZE,stdin);
if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
break;
write(sock,message,strlen(message));
str_len=read(sock,message,BUF_SIZE-1);
message[str_len]=0;
printf("Message from server : %s \n",message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
实现效果:
服务器端等待队列大小为5,意思是可以有5个客户端发起connect连接,它们的连接请求会被注册到服务器端等待队列,但只有第一个客户端会与服务器通信,后面的必须等待上一个客户端结束通信才可建立连接。
但因为TCP不存在数据边界
所以可能
write了好几次,却一次到达
read了好几次,却一次把数据都读了出来
也可能write了一次,却几次才到达
read了一次,却几次才把数据读完
(这是上面的程序存在的问题)