文章目录
一、引入
UDP和TCP的区别:
对于TCP协议有几个特点:
1️⃣ 传输层协议
2️⃣ 有连接(正式通信前要先建立连接)
3️⃣ 可靠传输(在内部帮我们做可靠传输工作)
4️⃣ 面向字节流
对于UDP协议有几个特点:
1️⃣ 传输层协议
2️⃣ 无连接
3️⃣ 不可靠传输
4️⃣ 面向数据报
可以看到TCP对比UDP会建立链接。
其他的接口跟UDP其实没什么区别:【网络编程】demo版UDP网络服务器实现
二、服务端实现
2.1 创建套接字socket
在通信之前要先把网卡文件打开。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
RETURN VALUE
On success, a file descriptor for the new socket is returned.
On error, -1 is returned, and errno is set appropriately.
这个函数的作用是打开一个文件,把文件和网卡关联起来。
参数介绍:
domain
:一个域,标识了这个套接字的通信类型(网络或者本地)。
只用关注上面两个类,第一个AF_UNIX
表示本地通信,而AF_INET
表示网络通信。
type
:套接字提供服务的类型。
这一章我们讲的式TCP,所以使用SOCK_STREAM
。
protocol
:想使用的协议,默认为0即可,因为前面的两个参数决定了,就已经决定了是TCP还是UDP协议了。
返回值:
成功则返回打开的文件描述符(指向网卡文件),失败返回-1。
而从这里我们就联想到系统中的文件操作,未来各种操作都要通过这个文件描述符,所以在服务端类中还需要一个成员变量表示文件描述符。
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include "log.hpp"
class TCPServer
{
static const uint16_t gport = 8080;
public:
TCPServer(cosnt uint16_t& port = gport)
: _sock(-1)
, _port(port)
{
}
void InitServer()
{
_sock = socket(AF_INET, SOCK_STREAM, 0);
if(_sockfd == -1)
{
std::cerr << "create socket error" << std::endl;
exit(1);
}
std::cout << "create socket success" << std::endl;
}
void start()
{
}
private:
int _sock;
uint16_t _port;
};
2.2 绑定bind
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
RETURN VALUE
Upon successful completion, bind() shall return 0;
otherwise, -1 shall be returned and errno set to indicate the error.
参数介绍:
socket
:创建套接字的返回值。
address
:通用结构体(【网络编程】socket套接字有详细介绍)。
address_len
:传入结构体的长度。
所以我们要先定义一个sockaddr_in
结构体填充数据,在传递进去。
然后就是跟UDP一样,先初始化结构体,再处理IP和端口。
要注意IP要绑定任意IP也就是INADDR_ANY
。
至于为什么再上一章【网络编程】demo版UDP网络服务器实现有过详细讲解。
void InitServer()
{
_sock = socket(AF_INET, SOCK_STREAM, 0);
if(_sockfd == -1)
{
std::cerr << "create socket error" << std::endl;
exit(1);
}
std::cout << "create socket success" << std::endl;
struct sockaddr_in si;
// 初始化结构体
bzero(&si, sizeof si);
si.sin_family = AF_INET;
si.sin_port = htons(_port);// 主机转网络序列
si.sin_addr.s_addr = INADDR_ANY;
if(bind(_sock, (struct sockaddr*)&si, sizeof si) < 0)
{
std::cout << "bind socket error" << std::endl;
exit(1);
}
std::cout << "bind socket success" << std::endl;
}
2.3 设置监听状态listen
TCP跟UDP的不同在这里就体现了出来。
要把socket套接字的状态设置为listen状态。只有这样才能一直获取新链接,接收新的链接请求。
举个例子:
我们买东西如果出现了问题会去找客服,如果客服不在那么就回复不了,所以规定了客服在工作的时候必须要时刻接收回复消息,这个客服所处的状态就叫做监听状态。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
RETURN VALUE
On success, zero is returned.
On error, -1 is returned, and errno is set appropriately.
关于第二个参数backlog后边讲TCP协议的时候介绍,目前先直接用。
static const int gbacklog = 10;
void InitServer()
{
_sock = socket(AF_INET, SOCK_STREAM, 0);
if(_sock == -1)
{
std::cerr << "create socket error" << std::endl;
exit(1);
}
std::cout << "create socket success" << std::endl;
struct sockaddr_in si;
// 初始化结构体
bzero(&si, sizeof si);
si.sin_family = AF_INET;
si.sin_port = htons(_port);// 主机转网络序列
si.sin_addr.s_addr = INADDR_ANY;
if(bind(_sock, (struct sockaddr*)&si, sizeof si) < 0)
{
std::cout << "bind socket error" << std::endl;
exit(1);
}
std::cout << "bind socket success" << std::endl;
// 设置监听状态
if(listen(_sock, gbacklog