using Socket in C++
来源:互联网 发布:c语言程序举例 编辑:程序博客网 时间:2024/05/17 06:55
先从底层说起,Socket是如何实现的?
图1,Linux下收包。可见Socket是由操作系统内核在MAC层——IP层——传输层上实现的。
真正从网卡(NIC)进来的数据是完整的MAC帧,底层用数据结构sk_buff 描述,最终进入接收缓冲区recv buffer,而我们应用层调用read / recv /recvfrom 从接收缓冲区拷贝数据到应用层提供的buffer,对一般的套接字,如SOCK_STREAM(TCP), SOCK_DGRAM(UDP) 来说,此时缓冲区只有user data,其他各层的头部已经被去除,而对于SOCK_RAW 来说是IP head + IP payload,当然也可以是arp/rarp 包,甚至是完整的帧(加上MAC头)。
下面介绍Socket编程的基本步骤:
涉及到的数据结构:
Sockaddr地址的统一表示:
- 地址家族号 short
- 地址具体值 14字节
struct sockaddr_in {//用于TCP/IP家族的地址表示
short sin_family; //地址家族号,为扩展提供可能,一般取0,表示TCP/IP家族
u_short sin_port; //端口号
struct in_addr sin_addr; //存ip地址,具体见下图
char sin_zero[8]; //填充位,取8个0
};
Host Entry:可通过gethostbyname(char *)获取hostent*;
struct
hostent{
char
* h_name; //地址正式名称
char
** h_aliases; //别名数组
short
h_addrtype; //地址类型,一般取AF_INET
short
h_length; //地址长度(bit)
char
** h_addr_list; //IP列表,网络字节序
#define h_addr h_addr_list[0]; //第一个地址
};
点分十进制和无符号长整型互相转换
- long inet_addr(char* ip)点分十进制转网络字节序
- char* inet_ntoa(in_addr)网络字节序转点分十进制
方法介绍:
服务器端的sockaddr_in.sin_addr可以用IN_ADDRANY
创建socket的参数:
- 协议簇的类型,即指定通信域现在为TCP/IP PF-INET。
- 传输层协议:SOCK_STREAM字节流(TCP)||SOCK_DGRAMA数据报文(UDP)
- 网络层协议:一般为0指ip
- 创建后会自动绑定ip和随机端口号
Bind:
- Socket描述符
- 网络进程的地址信息,包括ip,port等
- 地址信息这个结构体的长度
- 一定要注意,一般只在listen的服务器端口进行绑定。客户端和服务器并发新开的线程没有该过程,因为他们的交互是短暂的,用完就将资源交还给OS。服务器绑定是因为提供的服务是约定好的。
- 另外 绑定是包括ip的,这对多网卡服务器是有意义的。
Listen:成功返回1,失败小于0
- Socket描述符
- 等待队列的最大长度,最大20一般5-10
- 注意UDP方式则不需要提供该操作,因为是无连接的,接收方不需要等待。
- 服务器收到请求,队列不满,则放到缓冲队列,用accept调用。
Accept(默认阻塞):
- 服务器端用于listen的Socket描述符,以提供服务器的地址信息
- Sockaddr指针,所接收客户端的网络地址信息。是出参,只给定义即可
- 上述地址的长度信息
- 如果连接成功,系统将返回一个新的套接字描述符int,该套接字此后专用于与该客户机的数据交换。但需要注意,这返回的仅仅是新建好的一个套接字描述符,应该建立线程处理用户的请求。
- Accept会有阻塞,缓冲队列取不到东西,则一直等待,使得主进程无法继续进行。而listen只是表示开始监听,指定哪个地方是接收请求的,缓冲队列长度,指定完就返回,不存在阻塞。
Connect(默认阻塞):
- 客户机的socket标识符
- 服务器的网络地址信息
- 地址长度
Read/write/send/recv(均为默认阻塞):
- 自己的套接字标识符
- 读写缓冲区指针
- 缓冲区长度
Close:
- 关闭连接前应先终止连接
TCP通信,C++Code:
/*------------------------------------------* 程序:server.c* 目的: 分配一个套接字,然后反复执行如下几步:* (1) 等待客户的下一个连接* (2) 发送一个短消息给客户* (3) 关闭与客户的连接* (4) 转向(1)步* 命令行语法: server [ port ]* port – 服务器端监听套接字使用的协议端口号* 注意: 端口号可选。如果未指定端口号,服务器使用PROTOPORT中指定的缺省* 端口号*-----------------------------------------------*/ #include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h>#include <stdio.h>#include <string.h>#define PROTOPORT 5188 /* 监听套接字的缺省协议端口号 */#define QLEN 6 /* 监听套接字的请求队列大小 */int visits = 0; /* 对于客户连接的计数*/ main(argc,argc)int argc;char* argv[];{struct hostent *ptrh; /* 指向主机列表中一个条目的指针 */struct sockaddr_in servaddr; /* 存放服务器网络地址的结构 */struct sockaddr_in clientaddr; /* 存放客户网络地址的结构 */int listenfd; /* 监听套接字描述符 */int clientfd; /* 响应套接字描述符 */int port; /* 协议端口号 */int alen; /* 地址长度 */char buf[1000]; /* 供服务器发送字符串所用的缓冲区 */ /* 清空sockaddr结构 */memset( (char*)& servaddr, 0, sizeof(servaddr) ); servaddr.sin_family = AF_INET; /* 设置为因特网协议族 */servaddr.sin_addr.s_addr = INADDR_ANY; /* 设置本地IP地址 */ /* 检查命令行参数,若已指定,则使用该端口号,否则使用缺省端口号 */if (argc > 1){ port = atoi(argv[1]); /* 如果指定了端口号,就将它转换成整数 */} else { port = PROTOPORT; /* 否则,使用缺省端口号 */} /* 将本地地址绑定到监听套接字*/if ( bind( listenfd, (struct sockaddr *)& servaddr, sizeof(servaddr)) < 0) {fprintf(stderr, ”bind failed\n” );exit(1);} /* 开始监听,并指定监听套接字请求队列的长度 */if (listen(listenfd, QLEN) < 0) {fprintf(stderr, ”listen filed\n” );exit(1);} /* 服务器主循环—接受和处理来自客户端的连接请求 */while(1) { alen = sizeof(clientaddr); /* 接受客户端连接请求,并生成响应套接字 */ if((clientfd = accept( listenfd, (struct sockaddr *)& clientaddr, &alen)) < 0 ) { fprintf( stderr, “accept failed\n”); exit(1); } visits++; /* 累加访问的客户数 */ sprintf( buf, “this server has been contacted %d time \n”, visits ); send(clientfd, buf, strlen(buf), 0 ); /* 向客户端发送信息 */ closesocket( clientfd ); /* 关闭响应套接字 */}}
/*----------------------------------------------------* 程序: client.c* 目的: 创建一个套接字,通过网络连接一个服务器,并打印来自服务器的信息* 语法: client [ host [ port ] ]* host - 运行服务器的计算机的名字* port - 服务器监听套接字所用协议端口号* 注意:两个参数都是可选的。如果未指定主机名,客户使用localhost;如果未指定端口号, * 客户将使用PROTOPORT中给定的缺省协议端口号*----------------------------------------------------*/ #include <sys/types.h> /* 基本系统数据类型*/#include <sys/socket.h>/* UNIX下,套接字接口包含文件*/#include <netinet/in.h>/* Internet地址簇*/#include <arpa/inet.h>/* Internet定义*/#include <netdb.h> /* 网络数据库操作*/#include <stdio.h> /* 标准输入输出*/#include <string.h> /* 字符串处理*/#define PROTOPORT 5188 /*缺省协议端口号*/extern int errno;char localhost = “localhost”; /*缺省主机名*/ main(argc,argv)int argc;char *argv[];{struct hostent *ptrh; /* 指向主机列表中一个条目的指针 */struct sockaddr_in servaddr; /* 存放服务器端网络地址的结构 */intsockfd; /* 客户端的套接字描述符 */intport; /* 服务器端套接字协议端口号*/char* host; /* 服务器主机名指针 */int n; /* 读取的字符数 */char buf[1000] ; /* 缓冲区,接收服务器发来的数据 */ memset((char*)& servaddr,0,sizeof(servaddr)); /* 清空sockaddr结构 */servaddr.sin_family = AF_INET; /* 设置为因特网协议族 */ /* 检查命令行参数,如果有,就抽取端口号。否则使用内定的缺省值*/if (argc>2){ port = atoi(argv[2]); /* 如果指定了协议端口,就转换成整数 */}else { port = PROTOPORT; /* 否则,使用缺省端口号 */}if (port>0) /* 如果端口号是合法的数值,就将它装入网络地址结构 */ servaddr.sin_port = htons((u_short)port);else{ /* 否则,打印错误信息并退出*/fprintf(stderr,”bad port number %s\n”,argv[2]);exit(1);} /* 检查主机参数并指定主机名 */if(argc>1){host = argv[1]; /* 如果指定了主机名参数,就使用它 */}else{host = localhost; /* 否则,使用缺省值 */} /* 将主机名转换成相应的IP地址并复制到servaddr 结构中 *//* 从服务器主机名得到相应的IP地址 */ptrh = gethostbyname( host ); if ( (char *)ptrh == null ) { /* 检查主机名的有效性,无效则退出 */ fprintf( stderr, ”invalid host: %s\n”, host ); exit(1);}memcpy(&servaddr.sin_addr, ptrh->h_addr, ptrh->h_length );/* 创建一个套接字*/sockfd = SOCKET(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) { fprintf(stderr, ”socket creation failed\n” ); exit(1);}/* 请求连接到服务器 */if (connect( sockfd, (struct sockaddr *)& servaddr, sizeof(servaddr)) < 0) { fprintf( stderr,”connect failed\n” ); /*拒绝连接,报错退出 */exit(1);}/* 从套接字反复读数据,并输出到用户屏幕上 */n = recv(sockfd , buf, sizeof( buf ), 0 );while ( n > 0) { write(1,buf, n); n = recv( sockfd , buf, sizeof( buf ), 0 );} /* 关闭套接字*/closesocket( sockfd ); /* 终止客户程序*/exit(0);}参考:http://blog.csdn.net/jnu_simba/article/details/12371127 《浅谈原始套接字》
阅读全文
1 0
- using Socket in C++
- Using Cookie in C#
- Using C code in symbian
- bitonic_sort using mpi in c
- Using LAPACK in C/C++
- Using LAPACK in C/C++
- Socket In C
- [C/C++] Using `getopt' in c/c++
- Send File In C Socket
- Send File In C Socket
- socket in C and Java
- Using Properties in Objective-C Tutorial 边看边记
- Using C object files in Delphi
- Packet Sniffer Code in C using sockets
- Using CSharp (C#) code in Powershell scripts
- How to avoid using() mistake in C#?
- Financial Applications using Excel Add-in Development in C/C++
- Vista Goodies in C++: Using Glass in Your UI
- 生成多个git ssh密钥
- spring和springMVC父子容器的原理
- Android移动开发-音乐的示波器、均衡、重低音和音场的实现
- spring中如何直接注入session和request对像
- Jmeter正则表达式提取器,只提取中文问题
- using Socket in C++
- spring的bean一般是单例模式,那多线程是怎么解决的?
- Android studio中布局文件出现render problem问题
- TCP和UDP以及IP报文格式
- ThreadLocal原理解析
- Redis学习08——kes的通用操作
- 数学之美笔记2
- 作业
- Java并发编程:深入剖析ThreadLocal