tcp简单通信程序详解
本篇博客的目的是用socket套接字及接口实现一个简单的tcp聊天程序。
实现步骤
服务端:
- 创建套接字listen_sock
- 绑定地址信息(IP地址,端口号)
- 监听套接字listen_sock
- 与服务端建立链接
- 接收客户端发来的数据
- 向客户端发送数据
- 关闭套接字
客户端:
8. 创建套接字sockfd
9. 绑定地址信息(内部完成,不手动绑定)
10. 申请与客户端建立连接
11. 建立连接成功
12. 向服务端发送数据
13. 接收服务端发来的数据
14. 关闭套接字
服务端:
1.创建套接字socket
//1.创建监听套接字listen_sock,参数依次为版本IPv4,流式套接字,默认tcp
int listen_sock = socket(AF_INET,SOCK_STREAM,0);
if (listen_sock < 0)
{
perror("create socket failed!");
return -1;
}
2.绑定地址信息
//2.为服务器绑定地址信息,以后凡是向服务器发起连接请求,都会首先让listen_sock处理。
struct sockaddr_in server_addr; //sockaddr_in仅用于IPv4
socklen_t size = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));//先将命令行第二个参数(端口号)转为整形再转为网络字节序。
server_addr.sin_addr.s_addr = inet_addr(argv[1]);//将命令行第一个参数(ip地址)转为网络字节序。
int ret = bind(listen_sock,(struct sockaddr*)&server_addr,size);
这里创建一个地址信息块,存储了服务端的IP地址和端口号,在后面的bind函数中传入作为第二个参数,bind成功,就等同于向操作系统说明,只要是向这个IP地址和端口发送的数据,都先经过我listen_sock这里。
这里同时也要注意网络字节序的转换,我们先来看一看sockaddr_in的结构:
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
};
可以看出,port是无符号16位的整型,sin_addr则是4个字节的整型,
而在网络字节序的函数,我们应该选择的就是htons(host to net small )和inet_addr。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);(对无符号long4个字节转换为网络字节序的顺序)
uint16_t htons(uint16_t hostshort);(对无符号short2个字节转换为网络字节序的数据)
uint32_t ntohl(uint32_t netlong);(将4个字节的网络字节序数据转换为当前的主机字节序数据)
uint16_t ntohs(uint16_t netshort);将一个16位(2个字节)由网络字节序转变为主机字节序
in_addr_t inet_addr(const char *cp);:将一个十进制的字符串转换为互联网标准转换成,
int inet_pton(int af, const char *src, void *dst);//效果同inet_ntoa
3.开始监听socket
//3.监听listen_sock套接字
if (listen(listen_sock,5))
{
perror("listen error!");
return -1;
}
一旦开始监听,操作系统就会分配一块儿缓冲区作为连接成功队列,listen的第二个参数决定这这个队列的大小,也叫最大并发连接数。
这个队列非常重要,当一个链接到来时,listen_socket检查队列是否还有位置,有就将任务置入队列,创建一个新的socket与该客户端进行通信。
为什么要建立一个新的newsocket,而不直接用监听socket与客户端通信?
原因其实很简单,我们举个例子,我们去中介哪儿租房,中间见到我们,说,行,现在里屋还有位置,你进去里屋,我安排一个想出租房的人和你聊吧。在这里,我们对应着客户,中介对应着监听socket,里屋对应着链接成功队列,出租房子的人对应newsocket。如果我们和socket直接通信,那就等于和中介谈租房的事情,显然不合适。所以在程序中,监听套接字只处理和我们的建立连接的事务,而不亲自和我们通信,一旦链接建立成功,他就会创建一个新的套接字,也就是newsocket和我们通信。每个不同的客户对应不同的newsocket,大家互不打扰,这也侧面说明每条tcp通信都是1对1的。
为什么每个链接的目的ip和目的端口号都是相同的,服务器还是能每个都处理好呢?
答:注意!我们说的和客户端建立链接用的socket是监听socket,他只负责将这个任务注册进任务队列,并不去确定客户的原ip和原端口&#