这一讲通过一个简单的时间获取程序简单介绍套接字编程。

1、套接字API

1.1、套接字地址结构

上一讲中介绍了TCP的一些内容,知道了一个套接字对唯一标识了网络中的一个TCP连接,而一个套接字标识了一个TCP连接中的一端。套接字中包含本地IP地址和本地端口,由于现在的IP地址有IPv4和IPv6,因此套接字地址结构也就有两种。

1.1.1、IPv4套接字地址结构

IPv4套接字地址结构也叫“网际套接字地址结构”,包含在<netinet/in.h>头文件中,下面是它的定义:

<pre name="code" class="cpp">struct sockaddr_in {uint_8         sin_len;//地址长度sa_family_t    sin_family;//协议族:在这里是AF_INETin_port_in     sin_port;//16位端口号:网络字节序struct in_addrsin_addr;//32位IP地址结构:网络字节序char           sin_zero[8];//未使用
};
struct in_addr {in_addr_t      s_addr;//32位IP地址:网络字节序
};

可以知道,IPv4套接字地址结构的大小是16字节。注意:套接字地址结构只在主机上使用,虽然一些字段(比如IP地址和端口号)可以在不同主机间传递,但结构本身并不传递。

1.1.2、通用的套接字地址结构

当套接字作为参数传递进套接字函数时,套接字是作为引用的形式来传递的。下面是一个通用套接字地址结构,定义在<sys/socket.h>头文件中:

struct sockaddr {uint8_t     sa_len;//地址长度sa_family_t sa_family;//协议族char        sa_data;//数据:包括IP地址和端口号
};

这个结构的唯一作用就是对指向特定于协议的套接字地址结构的指针执行类型强制转换。

1.1.3、IPv6套接字地址结构

IPv4套接字地址结构也定义在<netinet/in.h>头文件中。

struct in6_addr {uint8_t          s6_addr[16];//128位IPv6地址:网络字节序
};#define SIN6_LENstruct sockaddr_in6 {uint8_t          sin6_len;//地址长度sa_family_t      sin6_family;//协议族:在这里是AF_INET6in_port_t        sin6_port;//端口号:网络字节序uint32_t         sin6_flowinfo;//未定义structin6_addr  sin6_addr;//IPv6地址结构uint32_t         sin6_scope_id;//地址范围
};

1.1.4、新的通用套接字地址结构

当需要用一个地址结构应用两种不同的IP地址时,这个新的通用套接字地址结构克服了sockaddr的缺点。定义在<netinet/in.h>头文件中:

struct sockaddr_storage {uint8_t     ss_len;//地址长度sa_family_tss_family;//协议族
};

sockaddr_storage能够满足任何对齐要求;

sockaddr_storage足够大,能容纳任何一种套接字地址结构;

除了ss_len和ss_family外,其它数据对用户透明。

1.2、值-结果参数

C 语言中,函数的返回值只能有一个,不过可以通过传递引用参数来达到返回多个结果的效果。C++中也有按引用传递参数,引用其实就是指针,由于实参与形参指 向同一块地址,因此在函数中对形参的修改也会反映到实参中,这样就达到了返回通过引用返回结果的效果。值-结果参数也是这样。在网络编程中,套接字地址结 构通常是值-结果参数。

当向套接字函数传递套接字地址结构时,总是以引用的形式来传递,作为参数,套接字地址结构中的地址长度告诉内核需要从进程中复制多少数据,避免了越界访问。如下图:

当函数返回时,内核将地址的真实大小放入长度中,因此长度又作为结果从内核中返回到进程中,告诉了进程这个结构究竟存储了多少数据。这种类型的参数叫做值-结果参数。如下图:

1.3、网络字节序与主机字节序

对于多字节数据,在内存中的存数方式有两种:小端字节序和大端字节序。

小端字节序:将低序字节存在起始地址;

大端字节序:将高序字节存在其实地址;

下图展示了两种格式:

举例来说:对于数字1,小端字节序为:

0

0

0

1

A+3        A+2        A+1          A

即,0x0001

而大端字节序为:

1

0

0

0

A+3        A+2        A+1         A

如果不转换的话,那么这个数就是0x1000

网络字节序使用大端字节序。所以需要在主机字节序和网络字节序间转换。下面是转换函数,定义在<netinet/in.h>头文件中:

uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);

前两个函数将主机字节序转换成网络字节序;后两个函数将网络字节序转换成主机字节序。

1.4、地址转换函数

下面的函数都在<arpa/inet.h>头文件中。
(1)inet_aton函数

函数将点分十进制地址转换成IPv4地址。定义如下:

int inet_aton(const char *strptr,struct in_addr*addrptr);

如果字符串有效就返回1,无效返回0.地址存在addrptr中,以网络字节序存储。

(2)inet_addr函数

函数将点分十进制地址转换成IPv4地址。定义如下:

in_addr_t inet_addr(const char *strptr);

如果有效返回IPv4地址,否则返回INADDR_NONE。函数已经被废弃。

(3)inet_ntoa函数

函数将IPv4地址转换成点分十进制地址字符串。定义如下:

cahr *inet_ntoa(struct in_addr inaddr);

参数的地址是网络字节序。

以下两个函数对IPv4和IPv6地址都适用。

(4)inet_pton函数

函数将IP地址转换成字符串。定义如下:

int inet_pton(int family,const char *strptr,void *addrptr);

如果成功返回1,输入无效返回0,出错返回-1。

(5)inet_ntop函数

函数与inet_pton函数操作相反。定义如下:

const char* inet_ntop(int family,const void *addrptr,char *strptr,size_t len);

如果成功返回指向结果的指针,出错返回NULL。

2、套接字函数

2.1、函数

套接字函数有下面几个,定义在<sys/socket.h>头文件中。
int socket(int family,int type,int protocol);//返回:成功返回非负描述符,出错返回-1
int connect(int sockfd,const sockaddr *servaddr,socklen_t addrlen);//返回:成功返回0,出错返回-1
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);//返回:成功返回0,出错返回-1
int listen(int sockfd,int backlog);//返回:成功返回0,出错返回-1
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);//返回:成功返回非负描述符,出错返回
int close(int sockfd);//返回:成功返回0,出错返回

2.2、套接字函数的调用过程

下面是一个完整的TCP连接中服务器与客户的函数调用过程:

3、一个简单的时间获取程序

下面的程序运用了上面讲述的内容,分为客户端程序和服务器端程序。代码如下:
(1)客户端
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 1024int main(int argc,char *argv[])
{int sockfd;char recvline[MAXLINE];if(argc!=2||strcmp(argv[1],"--help")==0){printf("Usage:%s <IPaddress>\n",argv[0]);return 0;}if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){printf("socket error\n");return 0;}struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(5000);if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0){printf("inet_pton error for %s\n",argv[1]);return 0;}if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){printf("connect error\n");return 0;}int n;while((n=read(sockfd,recvline,MAXLINE))>0){recvline[n]=0;if(fputs(recvline,stdout)==EOF){printf("fputs error\n");return 0;}}if(n<0){printf("read error\n");return 0;}return 0;
}

(2)服务器端

#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <time.h>
#define MAXLINE 1024int main(int argc,char *argv[])
{int listenfd;struct sockaddr_in servaddr;char buff[MAXLINE];time_t ticks;if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0){printf("socket error\n");return 0;}bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(5000);servaddr.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){printf("bind error\n");return 0;}if(listen(listenfd,5)<0){printf("listen error\n");return 0;}int connfd;socklen_t len;struct sockaddr_in cliaddr;for(;;){len=sizeof(cliaddr);printf("Listening...\n");if((connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&len))<0){printf("accept error\n");return 0;}printf("Receive a connection from:%s.%d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,buff,sizeof(buff)),ntohs(cliaddr.sin_port));ticks=time(NULL);snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks));if(write(connfd,buff,strlen(buff))<0){printf("write error\n");return 0;}if(close(connfd)<0){printf("close error\n");return 0;}}
}

运行结果如下:

(1)启动服务器
程序开始监听。
(2)启动客户端
得到结果
(3)服务器端

UNIX网络编程笔记(2):一个简单的时间获取程序相关推荐

  1. 进程fork和exec ---Unix网络编程笔记

    进程fork和exec ---Unix网络编程笔记 fork 一次调用,两次返回 fork的两个典型用法 最简单的并发服务器---fork子进程 exec -Unix网络编程笔记) fork #inc ...

  2. UNIX网络编程笔记(3):简单的并发服务器

    上一讲中的简单时间获取服务器是一个迭代服务器,对于获取时间来说够用了.迭代服务器有这样的特点:同一时间只能给一个客户服务.也就是说,如果某一时刻服务器与某个客户正在连接,其它客户必须等到上一个客户与服 ...

  3. UNIX网络编程笔记(4):简单的回射程序

    上一讲中我们通过调用fork函数实现了一个简单的并发时间获取服务器.这是一个简单的并发服务器框架,然而这里使用这个框架实现一个简单的回射服务器会出现一个问题,这个问题就是僵尸子进程. 1.回射程序 下 ...

  4. UNIX网络编程笔记(7):回射程序的UDP版本

    1.UDP简介 UDP是一个简单的传输层协议,应用进程往一个UDP套接字写入数据,随后被封装到一个UDP数据报,进而又被封装到一个IP数据报,然后发送到目的地.UDP不保证UDP数据报会最终到达目的地 ...

  5. 网络爬虫笔记 :一个简单的爬虫框架

    学了两节课的 Python 爬虫,也算是入门了吧.敲了两天的案例代码之后,我突然发现,这些代码虽然功能不同,写法各异,但是终归是有章可循的,整体框架是一致的.所以我自己整理了一个简单的爬虫框架,适合初 ...

  6. 【UNIX网络编程】| 【03】TCP客户/服务器程序示例

    文章目录 1.概述 2.TCP回射服务器程序 3.TCP回射客户程序 3.正常启动 4.正常终止 5.POSIX信号处理 5.1 signal函数 5.2 POSIX信号语义 6.处理SIGCHID信 ...

  7. UNIX网络编程笔记(6):I/O复用之select函数

    上一讲中我们正确处理了僵尸子进程,使得这个简单的服务器更加健壮.不幸的是,这个程序仍然有问题.想象一下,如果一个客户正在和一个服务器子进程连接建立完毕正在通话,而服务器子进程意外终止(比如kill), ...

  8. UNIX网络编程笔记(1):TCP简介

    1.简介 TCP(Transmission Control Protocol),即传输控制协议,是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP协议有以下几个特点: TCP提供客户与服务器 ...

  9. 【Python 基础】网络编程 - Python写一个简单的HTTP服务端和客户端,实现Client/Server交互

    1.HTTP 首先讲一下http和https,详细可以去看runoob http-vs-https 基本概念 HTTP(HyperText Transfer Protocol:超文本传输协议)是一种用 ...

最新文章

  1. LeetCode 191 Number of 1 Bits
  2. 依次提取二进制1位置的数字,
  3. 2015年240个数据库引擎排名参考
  4. 函数计算是如何工作的?
  5. SAP Spartacus routing parameter mapping介绍
  6. SQL SERVER 一个SQL语句的执行顺序
  7. 前端学习(592):使用snippets辅助debugging
  8. C语言的数组名和对数组名取地址
  9. 干货|MIT线性代数课程精细笔记6-子空间与零空间
  10. 在matlab中function,Matlab中function函数使用操作方法
  11. mysql 多键sequence_MySQL增多Sequence管理功能
  12. MySQL--Centos7下安装5.7.19
  13. 计算机基础和Linux安装
  14. matlab求hurst,请问如何用MATLAB计算大盘的HURST
  15. linux rapidsvn 使用教程,推荐一款简单方便的SVN客户端,在百度BAE中使用RapidSVN,百度BAE SVN教程...
  16. Keras 处理 不平衡的数据的分类问题 imbalance data 或者 highly skewed data
  17. 测试无线网网速软件,技术员教你解决怎么测试无线网络网速
  18. Go中rune类型浅析
  19. python宇晨_第三十届全国青少年科技创新大赛青少年科技创意作品中学组.PDF
  20. MAC系统免驱显卡汇总

热门文章

  1. 对JSON的一点认识和理解以及JQuery处理JSON
  2. 一个不错的讲解flex 3中自定义事件的文章
  3. java dubbo 方案,Missing artifact com.alibaba:dubbo:jar:2.8.4 dubbo解决方案
  4. aws ec2时间_AWS中自动化的三大领域,以避免支付过多的云账单
  5. 如何用python画组合图形_python结合G2绘制精美图形
  6. php 函数变量的顺序,PHP实现参数的自定义顺序调用 | 剑花烟雨江南
  7. 修改用户名_Word办公技巧:如何更改文档修订者的用户名?
  8. java线程实现排序_【多线程实现快速排序】
  9. java面向对象的三大特征是6_Java面向对象的三大特征
  10. java数组交集_java数组的交集和并集