旅游景点网站模板,文字logo免费设计在线生成,快速搭建网站视频教程,大连 手机网站案例Linux下套接字TCP实现网络通信 文章目录 Linux下套接字TCP实现网络通信1.引言2.具体实现2.1接口介绍1.socket()2.bind()3.listen()4.accept()5.connect() 2.2 服务器端server.hpp2.3服务端server.cc2.4客户端client.cc 1.引言
套接字(Socket)是计算机网络中实现网络通信的一…Linux下套接字TCP实现网络通信 文章目录 Linux下套接字TCP实现网络通信1.引言2.具体实现2.1接口介绍1.socket()2.bind()3.listen()4.accept()5.connect() 2.2 服务器端server.hpp2.3服务端server.cc2.4客户端client.cc 1.引言
套接字(Socket)是计算机网络中实现网络通信的一种编程接口。它提供了应用程序与网络通信之间的一座桥梁因为它允许应用程序通过网络发送和接收相应的数据以实现不同主机之间的通信。
通常套接字由以下两部分组成 1.网络IP和端口号IP用来标识主机而端口号可以标识到单台主机的唯一进程。 2.通信协议套接字通过规定通信协议来制定数据传输和发送的规则。常见的有TCP和UDP等协议。 TCP是一种面向连接的协议提供可靠的、有序的、基于字节流的数据传输。
UDP是一种无连接的协议提供不可靠的、无序的、基于数据报的数据传输。
今天我们来学习TCP实现网络通信。TCP由于能提供可靠、基于字节流的数据传输使用率与使用场景也比UDP多很多。
我们来看看能实现出什么样的结果(聊天室模拟两个用户随机通信)
若不开启服务端就只开启客户端的话那么就会像打游戏的某些情况连不上
当服务端和客户端都开启后就可以正常通信了 这里我们还是通过客户端给服务器端发送消息通过TCP链接实现通信。
那么事不宜迟我们马上开始分享实现过程吧
2.具体实现
2.1接口介绍
1.socket()
socket函数是用于创建套接字的函数创建成功返回文件描述符fd失败返回-1 int socket(int domain, int type, int protocol); 参数说明 domain 指定套接字的地址族Address Family 今天我们选择 AF_INETIPv4 地址族 type 指定套接字的类型Socket Type 今天我们选择 SOCK_STREAM有连接的字节流套接字用于TCP协议 protocol 可选参数指定具体的传输协议。常用的有 今天我们选择 0自动选择合适的协议 2.bind()
在Linux下bind() 函数用于将一个套接字socket与特定的IP地址和端口号进行绑定。 *int bind(int sockfd, const struct sockaddr addr,socklen_t addrlen); 参数说明
sockfd要进行绑定的套接字的文件描述符。addr指向一个 struct sockaddr 结构体的指针其中包含要绑定的IP地址和端口号信息。addrlenaddr 结构体的长度。
在绑定bind的第二个参数中我们也需要用到库中定义好的sockaddr_in结构体来初始化
具体结构体struct sockaddr_in说明:
结构体中有三个值也需要初始化指定一下 sin_family表示地址族Address Family一般为 AF_INET。 sin_port表示端口号。它是一个 16 位的整数使用网络字节序大端字节序表示。在使用时通常需要使用 htons() 函数将主机字节序转换为网络字节序。 sin_addr表示 IPv4 地址。它是一个 struct in_addr 类型的结构体用于存储 32 位的 IPv4 地址。 一般服务端用INADDR_ANY,让udp_server在启动时候可以绑定任何ip. 客户端用inet_addr函数将字符串转化成32位无符号整数 3.listen()
listen()函数将套接字设置为监听状态等待连接请求。
参数
sockfd套接字的文件描述符。这里我们选择前面socket创建好的返回值.backlog指定等待连接队列的最大长度。一般不会太大我们这里写32即可。 4.accept()
accept() 函数接受客户端的连接请求创建一个新的套接字用于与客户端进行通信。
参数
sockfd套接字的文件描述符。这里我们选择前面socket创建好的返回值.addr指向客户端地址的结构体指针。创建一个sockaddr的结构体强转一下(struct sockaddr*)即可。addrlen客户端地址结构体的字节大小。创建一个socklen_t类型的值用来计算结构体大小。 5.connect()
connect() 函数发起与远程主机建立TCP连接的请求。
参数
sockfd套接字的文件描述符。这里我们选择前面socket创建好的返回值.addr指向远程主机地址的结构体指针。创建一个sockaddr的结构体强转一下(struct sockaddr*)即可。addrlen远程主机地址结构体的字节大小。创建一个socklen_t类型的值用来计算结构体大小。 2.2 服务器端server.hpp
在整个服务器端server.hpp中我们需要创建tcpServer类并在类中建立这些成员监听套接字、端口号等.
在类中我们还需要初始化服务器InitServer()和启动服务器Start()两个接口。
服务器端具体实现思路是我们创建套接字socket()后开始绑定bind()之后监听listen(),监听成功后我们获取链接accept()即可。
思路简单,但是实现还需要很多事完成:
static const uint16_t defaultport 8081;
static const int backlog 32;
using func_t std::functionstd::string(const std::string);class tcpServer
{public:tcpServer(func_t func,uint16_t port defaultport):_func(func),_port(port),_quit(true){}~tcpServer() {}void InitServer(){//1.创建套接字_listensock socket(AF_INET,SOCK_STREAM,0);if(_listensock 0){std::cerr create socket error std::endl;exit(-1);}//2.绑定struct sockaddr_in local;memset(local,0,sizeof(local)); //清空结构体local.sin_family AF_INET;local.sin_port htons(_port); //主机转网络local.sin_addr.s_addr htonl(INADDR_ANY);if(bind(_listensock,(struct sockaddr*)local,sizeof(local)) 0){std::cerr bind error std::endl;exit(-2);}//3.监听(tcp)if(listen(_listensock,backlog) 0){std::cerr listen error std::endl;exit(-3);}}void Start(){_quit false; //运行时设置位运行状态即不退出的状态while(!_quit) //服务器死循环{struct sockaddr_in client;socklen_t len sizeof(client);//4.获取链接acceptint sock accept(_listensock,(struct sockaddr*)client,len);if(sock 0) {std::cerr accept error std::endl;continue;} //揽客的sock失败后继续即可//5.获取链接成功std::cout 获取链接成功 sock from _listensock std::endl;service(sock);}}void service(int sock) //服务{char buffer[1024];while(true){ssize_t s read(sock,buffer,sizeof(buffer)-1);if(s 0) //代表成功读取{buffer[s] 0;std::string res _func(buffer); //回调显示std::cout res std::endl;write(sock,res.c_str(),res.size());}else if(s 0) //代表读到文件结尾 在网络中就相当于对方关闭链接{close(sock);std::cout quit std::endl;break;}else //文件读取失败{close(sock);std::cerr read error std::endl;break;}}}private:uint16_t _port; //端口号int _listensock; //监听套接字bool _quit; //代表服务器没有运行的状态func_t _func; //回调包装器为了后面输出后回显
};2.3服务端server.cc
在服务端的主文件中我们直接包含上面的头文件。
我们期望的用法是./tcp_server port代表运行可执行文件后面需要带一个参数端口号
所以我们能够从用户中输入的port通过main函数中的**char* argv[]**参数列表中获取到。并传给tcpSercer类中初始化与启动服务器即可。
#include server.hpp
#includememory
using namespace std;static void usage(string proc) //使用手册代表运行可执行文件后面需要带一个参数端口号
{std::cout Usage:\n\t proc port\n std::endl;}std::string echo(const std::string message)//输出回显
{return message;
}//期望用法./tcp_server port
int main(int argc,char* argv[])
{if(argc ! 2) //输入的不是两个参数说明你不会用。输出使用手册{usage(argv[0]);exit(-1);}uint16_t port atoi(argv[1]); //强转成能够使用的类型unique_ptrtcpServer ts(new tcpServer(echo,port));//采用智能指针创建释放资源ts-InitServer();ts-Start();return 0;
}2.4客户端client.cc
在客户端中我们conncet尝试链接到服务器端这里需要做一个重连反馈正在尝试重连…
我们期望运行格式./client serverip serverport代表运行可执行文件后需要两个参数IP和端口
我们从用户输入的两个参数中传给main,并通过main参数char* argv[]参数列表获取到之后获取到直接转化即可。 static void usage(string proc)
{std::cout Usage:\n\t proc serverip serverport\n std::endl;}
//期望使用./client serverip serverport
int main(int argc,char* argv[])
{if(argc ! 3){usage(argv[0]);exit(-2);}string serverip argv[1]; //获取到参数uint16_t port atoi(argv[2]);//1.创捷套接字int sock socket(AF_INET,SOCK_STREAM,0);if(sock 0){std::cerr socket error std::endl;exit(-1);}//2.客户端需要链接服务器 --connectstruct sockaddr_in server; //memset(server,0,sizeof(server)); //清空结构体server.sin_family AF_INET;//初始化结构体server.sin_port htons(port);//server.sin_addr.s_addr inet_addr(serverip.c_str()); //客户端inet_aton(serverip.c_str(),(server.sin_addr));int cnt 5;while(connect(sock,(struct sockaddr*)server,sizeof(server)) ! 0) //如果绑定失败{sleep(1);std::cout正在尝试重连... 重连次数 cnt-- std::endl;if(cnt 0) break;} if(cnt 0){cerr 服务器连接失败endl;exit(-1);}//3.连接成功while(true) //连接成功后从客户端直接输入发送数据{string line;char buffer[1024];coutEnter ; getline(cin,line);write(sock,line.c_str(),line.size()); //给缓冲区写数据ssize_t s read(sock,buffer,sizeof(buffer) -1);if(s 0)//正常写{buffer[s] 0;cout server rcho buffer endl;}else if(s 0) //写结束{cerr server quit endl;break;}else{ //异常cerr read error endl;break;}}close(sock);//关闭套接字管不管都可以return 0;
}最后运行之后就能获得我们之前通信的结果了