TCP套接字编写,多进程多线程版本 Linux网络通信( 五 )


答案是肯定有的,socket创建的套接字是用来服务端本身进行绑定的 。因为UDP是面向数据报,无连接的,所以创建好一个套接字之后直接等待数据到来即可,而TCP是面向连接,需要等待连接的到来,并获取连接,普通的一个套接字是不能够进行连接的监听,这时就需要用的listen来对创建好的套接字进行设置,将其设置为监听状态,这样这个套接字就可以不断监听连接状态,如果连接到来了,就需要通过accept获取连接,获取连接后返回一个值,也是套接字,这个套接字是用来描述每一个建立好的连接,方便维护连接和给对端进行响应,后期都是通过该套接字对客户端进行通信,也就是对客户端进行服务 。所以说,开始创建的套接字是与自身强相关的,用来描述自身,并且需要进行监听,所以我们也会称这个套接字叫做监听套接字,获取到的每一个连接都用一个套接字对其进行唯一性标识,方便维护与服务 。一个通俗的类比,监听套接字好比是一家饭馆拉客的,不断地去店外拉客进店,拉客进店后顾客需要享受服务,这时就是服务员对其进行各种服务,服务员就好比是accept返回的套接字,此时拉客的不需要关心服务员是如何服务顾客的,只需要继续去店外拉客进入店内就餐即可 。基于TCP协议的套接字协议服务器整体框架封装一个类,来描述tcp服务端,成员变量包含端口号和监听套接字两个即可,ip像udp服务端一样,绑定INADDR_ANY,构造函数根据传参初始化port,析构的时候关闭监听套接字即可
#define DEFAULT_PORT 8080 // 默认端口号为8080#define BACK_LOG 5 // listen的第二个参数class TcpServer{public:TcpServer(int port = DEFAULT_PORT):_port(port),_listen_sock(-1){}~TcpServer(){if (_listen_sock >= 0) close(_listen_sock);}private:int _port;int _listen_sock;};服务端的初始化创建套接字创建套接字用到的是socket这个接口,具体介绍如下:
int socket(int domain, int type, int protocol);功能: 创建套接字参数: domain:协议家族,我们用的都是IPV4,这里会填AF_INETtype:协议类型 。可以选择SOCK_DGRAM(数据报,UDP)和SOCK_STREAM(流式服务,TCP)protocol:协议类别,这里填写0,根据前面的参数自动推导需要那种类型返回值: 成功返回一个文件描述符,失败返回-1代码如下:
bool TcpServerInit(){ // 创建套接字 _listen_sock = socket(AF_INET, SOCK_STREAM, 0); if (_listen_sock < 0){cerr << "socket creat fail" << endl;return false; } cout << "socket creat succes, sock: " << _listen_sock << endl;}绑定端口号绑定端口号需要用到bind这个接口:
int bind(int sockfd, struct sockaddr *my_addr, socklen_taddrlen);参数: sockfd:套接字 my_addr:这里传一个sockaddr_in的结构体,里面记录这本地的信息:sin_family(协议家族)、sin_port(端口号)和sin_addr(地址),用来进行绑定 addrlen:第二个参数的结构体的大小返回值: 成功返回0,失败返回-1这里端口号我们填充一个8080,协议家族填充的还是AF_INET,这里IP绑定一个字段叫INADDR_ANY(通配地址),值为0,表示取消对单个IP的绑定,服务器端有多个IP,如果指明绑定那个IP,那么服务端只能够从这个IP获取数据,如果绑定INADDR_ANY,那么服务端可以接受来自本主机任意IP对该端口号发送过来的数据填充好了这个结构体,我们需要它进行强转为struct sockaddr
注意: 因为数据是要发送到网络中,所以要将主机序列的端口号转为网络序列的端口号
绑定端口号,需要填充struct sockaddr_in这个结构体,里面有协议家族,端口号和IP,端口号根据用户传参进行填写,IP直接绑定

经验总结扩展阅读