基础知识:
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_tstruct 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");elseprintf("Network ordered integer addr: %#lx \n",conv_addr);conv_addr=inet_addr(addr2);if(conv_addr ==INADDR_NONE)printf("Error occured!\n");elseprintf("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");elseprintf("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");elseprintf("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 1024void 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了一次,却几次才把数据读完
(这是上面的程序存在的问题)