目录
《Linux高性能服务器编程》 中对数据传输的解释
在《Linux高性能服务器编程》的第五章中讲到:当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端必然错误的解释之。解决方法是:发送端总是将要发送的数据转换成大端字节序(网络字节序),接收端根据自身情况来决定是否将数据转换为小端字节序(主机字节序),或者不转换。
使用专用socket地址
首先选择地址族:AF_INET(IPV4),其次将IP地址和端口号都用网络字节序来表示。
IPV4专用socket地址结构体:
struct sockaddr_in
{
sa_family_t sin_family; // 地址族
u_int16_t sin_port; // 端口号
struct in_addr sin_addr; // IPV4地址结构体
};
struct in_addr
{
u_int32_t s_addr //这里要放IPV4的地址,要用网络字节序来表示。
}
服务器端
首先设置文件描述符属性(setsockopt()函数);
其次就是初始化socket结构体(地址族选择AF_INET(IPV4协议族)将端口号和IP地址都用网络字节序表示);INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。
最后将socket结构体所指的socket地址分配给为命名的文件描述符
服务器端代码如下:
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1)
{
perror("socket error:");
exit(1);
}
//一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。
int reuse_addr = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) ;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定所有网卡所有IP
servaddr.sin_port = htons(8000);
if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error: ");
exit(1);
}
绑定完之后将该文件描述符设置为监听文件描述符,监听任务由内核来处理,但是此时还不监听,不是堵塞状态:
ret = listen(listenfd, FDSIZE);
accept阻塞等待建立连接返回值是一个新的文件描述符,用来与之通信,之前的文件描述符还存在,并且继续监听客户端的连接请求:
int clifd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen = sizeof(cliaddr);
clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
socket选项
下面的两个系统调用是用来读取和设置socket文件描述符的属性的方法:
#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
sock 是目标文件描述符;
level 是指定要操作哪个协议,一般选择SOL_SOCKET(通用的socket选项,和协议无关);
optname 指定选项的名字;上文选择的是SO_REUSEADDR(重用本地地址);
option_value 是指被操作选项的值;
option_len 是指被操作选项的长度;
客户端
要将IP地址和端口号都以网络字节序来表示
转换完之后,connect()阻塞等待服务器的应答
客户端代码如下:
struct sockaddr_in servaddr;
int sockfd;
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr)); // bzero函数 清零
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); //将IP地址从字符串格式“127.0.0.1”转换成 网络地址格式 ,然后放在指定位置(&servaddr.sin_addr)
servaddr.sin_port = htons(8000); //htons将端口号从主机字节序转变成网络字节序
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); // 在这里阻塞等待服务器端应答
参考文献
《Linux高性能服务器编程》 游双 章节 5.1