最近在学习unix网络编程,从卷一开始看,跟着书,敲了下代码。在这里记录下遇到的问题。
前言
书中源码,采用了作者很多自己写的函数,都在udp.h这个文件里。直接调库的话,显得太业余,去读他的源码,对于刚开始的人来说也太难了吧。所以这里采用自己参考源码,微微修改的方式来学习。
代码
先贴上代码吧;
我自己把需要的宏定义模仿作者放在另一个头文件中。
mfs.h
#include<string.h>
#define MAXLINE 4096
#define bzero(ptr ,n) memset(ptr, 0,n);
#define LISTENQ 1024
#define MAXLINE 4096
server.cpp
#include "mfs.h"
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv){
int listenfd,connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd =socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr =htonl(INADDR_ANY);//host to net 32位long
servaddr.sin_port=htons(2020);//host to net 16位
bind(listenfd, ( struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd ,LISTENQ);
for( ; ; ){
connfd =accept(listenfd, (sockaddr *)NULL, NULL);
ticks =time(NULL);
//替代sprintf函数,第二个参数设置的缓冲区大小范围
snprintf(buff, sizeof(buff), "%.24s\r\n",ctime(&ticks));
write(connfd, buff, strlen(buff));
close(connfd);
}
}
client.cpp
#include <iostream>
#include <unistd.h>
#include "mfs.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc, char **argv){
int sockfd, n;
char recvline[MAXLINE +1];
struct sockaddr_in servaddr;
//输入格式错误
if(argc !=2){
cout<<"usage :a.out<IPaddress>"<<endl;
return -1;
}
//申请sockfd
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) <0){
perror("socket error");
return -1;
}
//memset初始化地址空间
bzero(&servaddr ,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port =htons(2020);
//inet_pton是一个IP地址转换函数,可以在将点分文本的IP地址转换为二进制网络字节序”的IP地址,把 argv[1] 转换后存放在 servaddr.sin_addr
if(inet_pton(AF_INET, argv[1],&servaddr.sin_addr) <=0){
perror("inet_pton error for %s",argv[1]);
return -1;
}
//参数sockfd 的socket 连至参数serv_addr 指定的网络地址。结构sockaddr请参考bind()。参数addrlen为sockaddr的结构长度。
if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) <0){
perror("connect error");
return -1;
}
while( (n= read(sockfd, recvline, MAXLINE)) >0){
recvline[n] =0;
if(fputs(recvline, stdout) ==EOF)
cout<<"fputs error"<<endl;
}
if(n<0)
cout<<"read error"<<endl;
exit(0);
}
编译
需要先编译mfs.h,ubuntu编译链接自己的头文件
编译server.cpp:g++ -o server server.cpp mfs.o
执行:./server
编译client.cpp :g++ -o client client.cpp mfs.o
执行:./client 127.0.0.1
遇到的问题
1、端口号原文用的是13
但1024以下的都需要root权限,所以我改成了2020。否则报错:connection refuse
2、客户端不需要blind,
blind 由bind() 函数的定义与作用——
将一本地地址与一套接口捆绑。本函数适用于未连接的数据报或流类套接口,在 connect()或listen()调用前使用。当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。
服务端指定了接受连接的端口,客户端只需要通过connect()知道怎么连接服务器就好,而对于本机采用哪个端口连接并不关心。如果客户端采用的blind的话,就绑定一端口,可能会造成端口冲突,(但也不是不行)
3、其他问题都在注释上了。
某些混乱的API
inet_pton和inet_ntop
用于点分字符类型到二进制网络字节序的转换
htonl、htons、ntohs等等
用于主机字节序和网络字节序之间的转换,转换前后都是unint_t
h代表主机字节序、n代表网络字节序,s代表短整型16位一般用于端口号的转换,l代表长整型32位,一般用于ipv4的转换。