目錄
前言
1.接口介紹
2.編寫服務器
3.編寫客戶端
4.編譯鏈接
5.測試
6.總結
前言
? ? ? ? 今天我們要介紹的是使用TCP協議實現數據通信,相比于之前寫的UDP服務器實現數據信,在主體邏輯上并沒有差別。客戶端向服務器發送信息,服務器接受信息并回顯,因為UDP是面向數據報,而TCP是面向連接的,所以在實現的時候接口上會有一些差別,下面,我們具體來看看UDP和TCP在編碼的實現上有什么不同。
1.接口介紹
因為TCP是面向連接的,所以服務器創建完套接字,然后綁定成功后,將套接字設置為監聽套接字
服務器啟動之后,首先需要根據監聽套接字建立連接,建立連接成功后返回一個新的文件描述符,后續的通信都是按照這個新的文件描述符按照讀寫文件的形式進行讀寫數據。
對于客戶端來說創建完套接字之后,客戶端啟動之后首先需要建立連接
listen():設置sock為監聽狀態
#include <sys/types.h> #include <sys/socket.h>int listen(int sockfd, int backlog);
sockfd:創建套接字的返回值
backlog:底層全連接隊列的長度
accept():服務端建立連接
#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:監聽套接字
struct sockaddr* addr:輸出型參數,可以獲取服務端的IP地址和port端口號
socklen_t* addrlen:結構體的大小
返回值:返回一個新打開的文件描述符
connect():客戶端建立連接
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd:創建套接字返回值
struct sockaddr* addr:輸出型參數,用來填寫需要訪問的服務端的IP地址和port端口號
socklen_t addrlen:結構體的大小
2.編寫服務器
tcpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "log.hpp"
namespace server
{using namespace std;enum{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR};static const uint16_t gport = 8080;static const int gback = 5;class TcpServer{public:TcpServer(const uint16_t &port = gport): _port(gport), _sock(-1){}void InitServer(){_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){logMessage(FATAL, "create socket error");exit(SOCKET_ERR);}logMessage(NORMAL, "create socket success");// 綁定:struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind socket error");exit(BIND_ERR);}logMessage(NORMAL, "bind socket success");// 設置sock為監聽狀態:if (listen(_sock, gback) < 0){logMessage(FATAL, "listen socket error");exit(LISTEN_ERR);}logMessage(NORMAL, "listen socket success");}void start(){for (;;){// 建立連接:struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_sock, (struct sockaddr *)&peer, &len); if (sock < 0){logMessage(ERROR, "accept error, next");continue;}logMessage(NORMAL, "accept a new link success");std::cout << "sock: " << sock << std::endl;//未來通信全部用sock,面向字節流的,后續全部都是文件操作:serviceIO(sock);close(sock);}}void serviceIO(int sock){char buffer[1024];while(true){ssize_t n = read(sock,buffer,sizeof(buffer)-1);if(n > 0){buffer[n] = 0;cout << "recvice message: " << buffer << endl;string outbuffer = buffer;outbuffer += "[server echo]";write(sock,outbuffer.c_str(),outbuffer.size());}else if(n == 0){// 代表client退出logMessage(NORMAL, "client quit, me too!");break;}}}~TcpServer(){}private:int _sock;uint16_t _port;};
}
tcpServer.cc:啟動服務器
#include"tcpServer.hpp"
#include<memory>
using namespace server;
static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc,char* argv[])
{if(argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);unique_ptr<TcpServer> tcs(new TcpServer(port));tcs->InitServer();tcs->start();return 0;
}
3.編寫客戶端
tcpClient.hpp
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>namespace client
{using namespace std;class TcpClient{public:TcpClient(const string& serverip,const uint16_t port):_serverip(serverip),_port(port),_sock(-1){}void InitClient(){_sock = socket(AF_INET,SOCK_STREAM,0);if(_sock < 0){cerr << "create sock fail" << endl;exit(-1);}}void start(){//建立連接:struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(_port);server.sin_addr.s_addr = inet_addr(_serverip.c_str());if(connect(_sock,(struct sockaddr*)&server,sizeof(server)) != 0){cerr << "connect fail" << endl;}else{string message;while(true){cout << "Please Enter: ";getline(cin,message);write(_sock,message.c_str(),message.size());char buffer[1024];int n = read(_sock,buffer,sizeof(buffer)-1);if(n > 0){buffer[n] = 0;cout << "Server回復: " << buffer << endl;}else{break;}}}}~TcpClient(){if(_sock >= 0)close(_sock);}private:string _serverip;uint16_t _port;int _sock;};
} // namespace client
tcpClient.cc:啟動客戶端
#include"tcpClient.hpp"
#include<memory>
using namespace client;
static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
int main(int argc,char* argv[])
{if(argc != 3){Usage(argv[0]);exit(-1);}uint16_t port = atoi(argv[2]);string ip = argv[1];unique_ptr<TcpClient> tcc(new TcpClient(ip,port));tcc->InitClient();tcc->start();return 0;
}
4.編譯鏈接
makefile:
.PHONY:all
all:tcpServer tcpClient
tcpServer:tcpServer.ccg++ -o $@ $^ -std=c++11
tcpClient:tcpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm tcpServer tcpClient
5.測試
?如圖所示,服務端和客戶端可以完成正常的數據通信了。
6.總結
????????TCP協議和UDP協議在數據通信的實現中,除了一些接口使用的不同之外,其實并沒有太大的不同,在之前說的UDP是面向數據報的而TCP是面向字節流的,這些特性又是如何體現的呢?關于這個問題,博主將在后面的文章中會為大家繼續進行介紹。不要錯過哦!