TCP服務(wù)器實(shí)現(xiàn)數(shù)據(jù)通信
前言
今天我們要介紹的是使用TCP協(xié)議實(shí)現(xiàn)數(shù)據(jù)通信,相比于之前寫的UDP服務(wù)器實(shí)現(xiàn)數(shù)據(jù)信,在主體邏輯上并沒有差別??蛻舳讼蚍?wù)器發(fā)送信息,服務(wù)器接受信息并回顯,因?yàn)閁DP是面向數(shù)據(jù)報(bào),而TCP是面向連接的,所以在實(shí)現(xiàn)的時(shí)候接口上會有一些差別,下面,我們具體來看看UDP和TCP在編碼的實(shí)現(xiàn)上有什么不同。
1.接口介紹
因?yàn)門CP是面向連接的,所以服務(wù)器創(chuàng)建完套接字,然后綁定成功后,將套接字設(shè)置為監(jiān)聽套接字
服務(wù)器啟動之后,首先需要根據(jù)監(jiān)聽套接字建立連接,建立連接成功后返回一個(gè)新的文件描述符,后續(xù)的通信都是按照這個(gè)新的文件描述符按照讀寫文件的形式進(jìn)行讀寫數(shù)據(jù)。
對于客戶端來說創(chuàng)建完套接字之后,客戶端啟動之后首先需要建立連接
listen():設(shè)置sock為監(jiān)聽狀態(tài)
#include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog);
sockfd:創(chuàng)建套接字的返回值
backlog:底層全連接隊(duì)列的長度
accept():服務(wù)端建立連接
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:監(jiān)聽套接字
struct sockaddr* addr:輸出型參數(shù),可以獲取服務(wù)端的IP地址和port端口號
socklen_t* addrlen:結(jié)構(gòu)體的大小
返回值:返回一個(gè)新打開的文件描述符
connect():客戶端建立連接
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd:創(chuàng)建套接字返回值
struct sockaddr* addr:輸出型參數(shù),用來填寫需要訪問的服務(wù)端的IP地址和port端口號
socklen_t addrlen:結(jié)構(gòu)體的大小
2.編寫服務(wù)器
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"); // 設(shè)置sock為監(jiān)聽狀態(tài): 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,面向字節(jié)流的,后續(xù)全部都是文件操作: 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:啟動服務(wù)器
#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回復(fù): " << 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.cc g++ -o $@ $^ -std=c++11 tcpClient:tcpClient.cc g++ -o $@ $^ -std=c++11 .PHONY:clean clean: rm tcpServer tcpClient
5.測試
如圖所示,服務(wù)端和客戶端可以完成正常的數(shù)據(jù)通信了。
6.總結(jié)
TCP協(xié)議和UDP協(xié)議在數(shù)據(jù)通信的實(shí)現(xiàn)中,除了一些接口使用的不同之外,其實(shí)并沒有太大的不同,在之前說的UDP是面向數(shù)據(jù)報(bào)的而TCP是面向字節(jié)流的,這些特性又是如何體現(xiàn)的呢?關(guān)于這個(gè)問題,博主將在后面的文章中會為大家繼續(xù)進(jìn)行介紹。不要錯(cuò)過哦!
到此這篇關(guān)于TCP服務(wù)器實(shí)現(xiàn)數(shù)據(jù)通信的文章就介紹到這了,更多相關(guān)TCP服務(wù)器數(shù)據(jù)通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++實(shí)現(xiàn)TCP客戶端及服務(wù)器Recv數(shù)據(jù)篩選處理詳解
- TCP服務(wù)器實(shí)現(xiàn)數(shù)據(jù)通信
- Python基于socket實(shí)現(xiàn)TCP/IP客戶和服務(wù)器通信
- C++多線程實(shí)現(xiàn)TCP服務(wù)器端同時(shí)和多個(gè)客戶端通信
- Java利用TCP協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器通信(附通信源碼)
- java Tcp通信客戶端與服務(wù)器端實(shí)例
- java模擬TCP通信實(shí)現(xiàn)客戶端上傳文件到服務(wù)器端
- Python socket網(wǎng)絡(luò)編程TCP/IP服務(wù)器與客戶端通信
- C#基于TCP協(xié)議的服務(wù)器端和客戶端通信編程的基礎(chǔ)教程
相關(guān)文章
采用軟件負(fù)載均衡器實(shí)現(xiàn)web服務(wù)器集群(iis+nginx)
我用nginx實(shí)現(xiàn)網(wǎng)站負(fù)載均衡測試的例子,windows下IIS做負(fù)載實(shí)測2016-04-04服務(wù)器(VPS)安裝WebSite Panel面板教程(圖文)
這篇文章主要為大家分享下WebsitePanel的安裝方法,WebsitePanel簡稱WSP是微軟旗下,開源免費(fèi)的虛擬主機(jī)系統(tǒng),需要win2008 r2環(huán)境2013-12-12linux 自動化運(yùn)維工具ansible的使用詳細(xì)教程
這篇文章主要介紹了自動化運(yùn)維工具ansible的使用詳細(xì)教程的相關(guān)資料,需要的朋友可以參考下2016-02-02解析服務(wù)器常見錯(cuò)誤代碼500、501、502、503、504、505
這篇文章主要介紹了服務(wù)器常見錯(cuò)誤代碼500、501、502、503、504、505的相關(guān)知識,需要的的朋友參考下吧2017-05-05服務(wù)器的硬件配置經(jīng)驗(yàn)分享(如何正確配置服務(wù)器以提高網(wǎng)站性能)
服務(wù)器的配置是互聯(lián)網(wǎng)技術(shù)領(lǐng)域中非常重要的一環(huán),一個(gè)合理配置的服務(wù)器可以提高系統(tǒng)的性能和穩(wěn)定性,保證用戶的訪問體驗(yàn),在本文中,我將介紹服務(wù)器配置的具體步驟和流程2023-08-08