UNIX网络编程 第一章

UNIX网络编程 第一章

1 一个简单的时间获取客户端程序

#include<unp.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>\n");
  }
  if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){//创建网际字节流套接字
    err_sys("socket error\n");
  }
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family=AF_INET;//地址簇
  servaddr.sin_port=htons(13);//将端口号转换为正确的格式
  if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0){//将点分十进制argv[1]转换为正确的ip地址格式
    err_quit("inet_pton error for %s\n",argv[1]);
  }
  if(connect(sockfd,(SA*)&servaddr,sizeof(servaddr))<0){//与指定地址建立连接
    err_sys("connect error\n");
  }
  while((n=read(sockfd,recvline,MAXLINE))>0){
    recvline[n]=0;//结尾符
    if(fputs(recvline,stdout)==EOF){
      err_sys("fputs error\n");
    }
  }
  if(n<0){
    err_sys("read error\n");
  }
  exit(0);//进程结束的时候自动释放sockfd文件描述符,退出连接
}

注意:AF_INET表示地址簇是网际地址,SOCK_STREAM表示socket函数创建的是字节流套接字
注意:socket函数的原型是:int socket(int domain, int type, int protocol);

domain是指会话domain,这个参数确定会话协议簇的选择。会话协议簇都定义在了sys/socket.h头文件中,当前可用的格式有:
AF_UNIX,AF_LOCAL,本地会话
AF_INET,IPv4因特网协议
AF_INET6,IPv6因特网协议
AF_IPX,IPX-Novell协议
AF_NETLINK,内核用户接口设备
AF_X25,ITU-T X.25/ISO-8208协议
AF_AX25,业余无线电AX.25协议
AF_ATMPVC,原始ATM PVCs接入
AF_APPLETALK,AppleTalk
AF_PACKET,低级分组接口

type参数指定了会话的语义,现在定义的有:
SOCK_STREAM,提供了连续的,可靠的,双工的,面向连接的字节流
SOCK_DGRAM,支持数据报文(无连接,不可靠,具有固定最大长度)
SOCK_SEQPACKET,提供一个连续的可靠的,基于固定最大长度的数据包传输路径的双向连接
SOCK_RAW,提供一个原始网络协议接入
SOCK_RDM,提供一个可靠的不保证按序到达的数据报服务
SOCK_PACKET,禁用该语义类型

protocol指明了socket使用的协议。

注意:

#include<arpa/inet.h>
uint32_t htonl(uint32_t hostlong);

//该函数无符号整形hostlong从主机字节序转换为网络字节序,同理类推下面

uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

//在i386架构中,主机字节序被定义为最不重要字节优先,网络字节序被定义为最重要字节优先

注意:

#include<arpa/inet.h>
int inet_pton(int af,const char*src,void *dst);

这个函数将src指定的字符串转换成af地址协议簇指定的网络地址结构中,然后将转换结构复制到dst中,网络地址簇只能是AF_INET和AF_INET6。
AF_INET地址簇:
src表示点分十进制IPv4地址,src被转换为struct in_addr类型,并且被复制到长度为sizeof(struct in_addr)的dst中。
inet_pton的返回值如果是1表示成功转换,如果是0表示src表示的不是一个正确的IP地址,如果地址簇af不是一个有效的地址簇就返回-1。

注意:

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

connect系统调用连接描述符sockfd指定的套接字,该套接字连接的地址为addr.addrlen表示addr的字节数.addr的格式由sockfd的地址空间决定。如果socket是一个SOCK_STREAM或SOCK_SEQPACKET,这个调用将会连接到addr指定的地址的socket。
如果连接成功返回0,如果连接失败返回-1。

2 一个时间服务器程序

#include<unp.h>
#include<time.h>

int main(int argc,char**argv){
  int listenfd,connfd;
  struct sockaddr_in servaddr;
  char buff[MAXLINE];
  time_t ticks;

  if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0){
    err_quit("建立监听描述符失败\n");
  }
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family=AF_INET;//设置套接字地址簇
  servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//在任意接口接受连接
  servaddr.sin_port=htons(13);

  if(bind(listenfd,(SA*)&servaddr,sizeof(servaddr))<0){
    perror("错误原因:");
    err_quit("将地址绑定到监听套接字失败\n");
  }
  if(listen(listenfd,LISTENQ)<0){//将绑定了套接字地址结构的网际字节流套接字描述符转换成监听套接字
    err_quit("转换成监听套接字失败\n");
  }
  for(;;){
    if((connfd=accept(listenfd,(SA*)NULL,NULL))<0){
      err_quit("获取连接失败\n");
    }
    ticks=time(NULL);
    snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks));
    if(write(connfd,buff,strlen(buff))<0){
      err_quit("服务器将时间写回客户端失败!\n");
    }
    close(connfd);
  }
}

注意:

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

当socket建立好的时候,该套接字存在于地址簇命名空间当中,但是并没有地址结构赋值给他,bind函数将addr表示的地址赋值给sockfd代表的套接字。addrlen表示addr结构的字节数。通常bind函数的该操作被称作是”将地址绑定到socket”。
在一个SOCK_STREAM地址簇的套接字获得客户端的连接之前一定要使用bind函数将地址结构类型的参数addr绑定到套接字中。
将地址绑定到sockfd的规则因地址簇不同而有所不同,sockaddr通常被定义为:

struct sockaddr{
sa_family_t sa_family;
char sa_data[4];
}

该结构的意义只在于将addr转换为通用地址指针从而避免编译错误。
bind调用如果成功将返回0,如果失败将返回-1。
经过测试,在linux中,该函数的调用必须具有超级管理员权限。

注意:

#include<sys/types.h>
#include<sys/socket.h>
int listen(int sockfd,int backlog);

listen函数将sockfd标志成为一个被连接的套接字,该套接字将会被用于被accept函数调用,等待connect请求。
sockfd的类型是:SOCK_STREAM或SOCK_SEQPACKET。
backlog参数定义了客户端等待连接队列的最大长度。如果一个连接达到的时候,连接队列已经满了,那么客户端将会收到一个ECONNREFUSED错误,如果底层协议支持重传,请求将会被忽略以等待其他连接退出之后能够连接成功。
成功连接返回0,失败返回-1。

**注意**:
#include<sys/type.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr*addr,socklen_t *addrlen);

该函数的第一个参数指定处于监听转台的流套接字,操作系统利用第二个参数来返回新创建的套接字的地址结构,操作系统用第三个参数来返回新创建的套接字的地址结构的长度。
本函数将会阻塞,知道有客户端请求到达。然后监听的sockfd就会创建返回一个新的套接字描述符,这个套接字指向第二个参数addr,该结构赋予客户端的套接字,即客户端的IP地址和端口号,知道了客户端地址然后就可以开始传输数据,在面向连接中,有send(int sockfd,const void*msg,int len,int flags);其中这里的参数sockfd就是新创建的套接字,也是客户端的地址(IP和port),recv函数同样道理。

注意:

#include<sys/types.h>
#include<sys/socket.h>
ssize_t send(int sockfd,const void*buf,size_t len,int flags);
ssize_t sendto(int sockfd,const void*buf,size_t len,int flag,const struct sockaddr*dest_addr,socklent_addrlen);
ssize_t sendmsg(int sockfd,const struct msghdr*msg,int flags);

这三个系统调用都是用于给另外一个套接字发送消息。
send只能用于已经接受了连接的socket(这样才知道消息的接受者是谁),send和write的唯一区别在于是否接受flags参数,如果flags=0,那么send和write是等价的。
同样,sendto(sockfd,buf,len,flags,NULL,0)也等价与send(sockfd,buf,len,flags);
如果sendto的sockfd是一个已经接受连接的socket描述符,那么dest_addr,addrlen参数被忽略了,并且,如果dest_addr和adddrlen不分别为NULL和0的话,EISCONN错误将有可能会被返回,如果sockfd并未真正连接的话,ENOTCONN错误将会返回。否则目标地址将由dest_addr,addrlen指定。对于sendmsg,目标地址由msg.msg_name指定,其长度由msg.msg_namelen指定。
对于send和sendto函数,发送的消息从buf参数中取得,对于sendmsg,消息由msg.msg_iov指定。sendmsg允许发送控制信息。
如果消息长度超过了底层协议的最大传输单元,EMSGSIZE错误将会返回,该消息讲不会被发送。
flags参数可以取得的值有:
bitwise OR zero。
MSG_CONFIRM
MSG_DONTROUTE
MSG_DONTWAIT
MSG_EOR
MSG_MORE
MSG_NOSIGNAL
MSG_OOB

senmsg函数的msghdr结构如下

struct msghdr{
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
size_t msg_controllen;
int msg_flags;
}

注意:

#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd,void*buf,size_t len,int flags);
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen);
ssize_t recvmsg(int sockfd,struct msghdr*msg,int flags);

这些函数被用于从sockfd中接收参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值