Chinaunix首页 | 论坛 | 博客
  • 博客访问: 60580
  • 博文数量: 30
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 301
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 22:19
个人简介

技术成就一切

文章分类

全部博文(30)

文章存档

2019年(1)

2015年(3)

2014年(24)

2013年(2)

我的朋友

分类: C/C++

2013-08-31 23:46:37

TCP时面向连接的通信协议,采用客户机-服务器模式,套接字的全部工作流程如下:
  1. 首先,服务器启动进程,调用socket创建一个基于TCP协议的流套接字描述符;
  2. 其次,服务进程调用bind命名套接字,将套接字描述符绑定到本地地址和本地端口上,至此,socket的半相关协议-{协议,本地地址,本地端口}-完成;
  3. 再次,服务器端调用listen,开始侦听客户端的连接请求;
  4. 接下来,客户端创建套接字描述符,并且调用connect向服务器提出连接请求,服务器端接受到客户端连接请求后,调用accept接受,并创建一个新的套接字描述符与客户端建立连接,然后原套接字描述符继续侦听客户端的连接请求;
  5. 客户端与服务器端的新套接字进行数据传输,调用write和send向对方发送数据,调用read和recv接受数据;
  6. 在数据交流完毕后,双方调用close和shutdown关闭套接字。    
  • socket连接
            #include
            #include
            int socket(int domain, int type, int protocol )
           参数说明:domain指定发送通信的域,有两种选择:AF_UNIX,本地主机通信,功能和IPC对象类似;AF_INET,Internet地址IPV4协议,在实际编程中,我们只使用AF_INET,如果需要与本地主机进程建立连接,只需把远程地址设置为‘127.0.0.1'即可;    
            参数type指定了通信类型,SOCK_STREAM(流套接字(它提供了双向的,可靠地,顺序的,不重复的,面向连接的通信数据流,它使用了TCP协议保真了数据传输的的正确性)),SOCK_DGRAM(提供一种独立的,无序的,不保证可靠地无连接服务,使用UDP协议,该协议不维护一个连接,它只把数据打成一个包,再把远程的IP贴上去,然后把这个包发送出去),SOCK_RAW(原始套接字,主要应用于底层协议的开发,进行底层的操作),协议AF_INET支持以上三种类型,而协议AF_UNIX不支持原始套接字;
  • socket的命名
            函数band命名一个套接字,它为该套接字分配一个半相关属性,原型如下:
           #include
           #include
           int bind( int sockfd, struct sockaddr *my_addr, socklen_t addrlen );
           参数说明:sockfd指定了套接字描述符,该值由socket返回,指针my_addr指向通用套接字的协议地址结构,addrlen确定my_addr缓冲区的长度;
           返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
           结构sockaddr描述了通用套接字的相关属性, 结构如下:
           typedef unsigned short int sa_family_t;
            #define __SOCKADDR_COMMON(sa_prefix) sa_family_t sa_prefix##family
            struct sockaddr{
                __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
                char sa_data[14]; /* Address data. */
            };
            
            不同的协议有不同的地址描述方式, 为了便于编码处理, 每种协议族都定义了自给的套接字地址属性结构, 协议族AF_INET使用结构sockaddr_in描述套接字地址信息, 结构如下:
            struct sockaddr_in{
                __SOCKADDR_COMMON (sin_);
                in_port_t sin_port; /* Port number. */
                struct in_addr sin_addr; /* Internet address. */
                /* Pad to size of `struct sockaddr'. */
                unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - \
                sizeof (in_port_t) - sizeof (struct in_addr)];
            };
            typedef uint32_t in_addr_t;
            struct in_addr{
                in_addr_t s_addr;
            };
            这里有两点需要注意:
            a. IP地址转换
                在套接字的协议地址信息结构中, 有一个描述IP地址的整型成员. 我们习惯使用点分方式描述IP地址, 所以需要将其转化为整型数据, 下列函数完成此任务
                #include
                #include
                #include
                int inet_aton(const char *cp, struct in_addr *inp);
                in_addr_t inet_addr(const char *cp);
                char *inet_ntoa(struct in_addr in);
            b. 字节顺序转换
网络通信常常跨主机, 跨平台, 跨操作系统, 跨硬件设备, 但不同的CPU硬件设备, 不同的操作系统对内存数据的组织结构不尽相同. 在网络通信中, 不同的主机可能采取了不同的记录顺序, 如果不做处理, 通信双方对相同的数据会有不同的解释. 所以需要函数实现主机字节顺序和网络字节顺序的转换
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
函数htons, htonl分别将16位和32位的整数从主机字节顺序转换为网络字节顺序.
函数ntohs, ntohl分别将16位和32位的整数从网络字节顺序转换为主机字节顺序。
  • (3) Socket的侦听
    TCP的服务器端必须调用listen才能使套接字进入侦听状态, 原型如下
#include
int listen(int s, int backlog);
参数说明:    参数s是调用socket创建的套接字. 参数backlog则确定了套接字s接收连接的最大数目.


  • 在TCP通信模型中, 服务器端进程需要完成创建套接字, 命名套接字和侦听接收等一系列操作才能接收客户端连接请求. 下面设计了一个封装了以上三个操作的函数, 代码如下        
int CreateSock(int *pSock, int nPort, int nMax){
struct sockaddr_in addrin;
struct sockaddr *paddr = (struct sockaddr *)&addrin;
int ret = 0; // 保存错误信息
if(!((pSock != NULL) && (nPort > 0) && (nMax > 0))){
printf("input parameter error");
ret = 1;
}
memset(&addrin, 0, sizeof(addrin));
addrin.sin_family = AF_INET;
addrin.sin_addr.s_addr = htonl(INADDR_ANY);
addrin.sin_port = htons(nPort);
// 创建socket, 在我本机上是5
if((ret == 0) && (*pSock = socket(AF_INET, SOCK_STREAM, 0)) <= 0){
printf("invoke socket error\n");
ret = 1;
}
// 绑定本地地址
if((ret == 0) && bind(*pSock, paddr, sizeof(addrin)) != 0){
printf("invoke bind error\n");
ret = 1;
}
if((ret == 0) && listen(*pSock, nMax) != 0){
printf("invoke listen error\n");
ret = 1;
}
close(*pSock);
return(ret);
}

































阅读(2058) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~