国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器

這篇具有很好參考價值的文章主要介紹了網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

簡單TCP服務(wù)器的實現(xiàn)

  • TCP區(qū)別于UDP在于要設(shè)置套接字為監(jiān)控狀態(tài),即TCP是面向鏈接,因此TCP套接字需要設(shè)置為監(jiān)聽狀態(tài)
void initserver()
{
//1.創(chuàng)建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
    logMessage(FATAL,"create listensocket error");
    exit(SOCK_ERR);
}
 logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//綁定失敗
{
    logMessage(FATAL,"bind error");
    exit(BIND_ERR);
}
 logMessage(NORMAL,"bind success");
//3.將套接字設(shè)置為監(jiān)聽模式
if(listen(_listensock,0)<0)
{
    logMessage(FATAL,"listen error");
    exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}

socket函數(shù)原型

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
  • domain 表示協(xié)議族,常用的有 AF_INET(IPv4)和 AF_INET6(IPv6)。

  • type 表示Socket類型,常用的有 SOCK_STREAM(TCP)和 SOCK_DGRAM(UDP)。

  • protocol 通??梢栽O(shè)置為 0,讓系統(tǒng)根據(jù) domaintype 來選擇合適的協(xié)議。

  • socket()打開一個網(wǎng)絡(luò)通訊端口,如果成功的話,就像open()一樣返回一個文件描述符

  • 應(yīng)用程序可以像讀寫文件一樣通過socket函數(shù)用read/write在網(wǎng)絡(luò)上收發(fā)數(shù)據(jù)

bind函數(shù)原型

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd 是socket描述符。

  • addr 是一個 struct sockaddr 結(jié)構(gòu)體,包含要綁定的IP地址和端口信息。

  • addrlenaddr 結(jié)構(gòu)體的長度。因為addr結(jié)構(gòu)體可以接受多種協(xié)議的sockaddr結(jié)構(gòu)體,因此要傳其結(jié)構(gòu)體的長度

  • bind()成功返回0,失敗返回-1。

  • bind()的作用是將參數(shù)sockfd和myaddr綁定在一起, 使sockfd這個用于網(wǎng)絡(luò)通訊的文件描述符監(jiān)聽addr所描述的地址和端口號;

listen函數(shù)原型

int listen(int sockfd, int backlog);
  • sockfd 是socket描述符,指用于進行網(wǎng)絡(luò)監(jiān)聽的文件描述符
  • backlog 表示等待連接隊列的最大長度。
  • listen成功返回0,失敗返回-1
  • listen函數(shù)將使得sockfd處于監(jiān)聽狀態(tài),并且允許backlog個客戶端處于連接等待狀態(tài),當收到多于backlog個客戶端的的連接請求則選擇忽略。
  • 實際上listen函數(shù)告訴操作系統(tǒng)指定的套接字sockfd處于監(jiān)聽狀態(tài),該套接字開始等待其他計算機通過網(wǎng)絡(luò)與其建立連接,一旦有連接請求到達,操作系統(tǒng)會將連接請求放入連接隊列中,連接隊列的最大長度為backlog,連接隊列是一個存放連接請求的緩沖區(qū),如果隊列已滿新的連接請求將會被拒絕。即當一個套接字處于監(jiān)聽狀態(tài)時,它不直接處理數(shù)據(jù)傳輸,而是等待其他計算機發(fā)起連接。

總的來說initserver函數(shù)作用是先創(chuàng)建套接字,然后填充指定的端口號和ip,并將套接字設(shè)置為監(jiān)聽狀態(tài)

void start()
{
    while(true)
    {
        struct sockaddr_in cli;
        socklen_t len=sizeof(cli);
        bzero(&cli,len);

        int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
        if(sock<0)
        {
            logMessage(FATAL,"accept client error");
            continue;
        }
        logMessage(NORMAL,"accept client success");

        cout<<"accept sock: "<<sock<<endl;
        }

accept函數(shù)原型

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd:是一個已經(jīng)通過 socket 函數(shù)創(chuàng)建的套接字描述符,并且是已經(jīng)處于監(jiān)聽狀態(tài),用于監(jiān)聽傳入的連接請求。

  • addr:是一個指向 struct sockaddr 結(jié)構(gòu)的指針,用于接收連接請求的客戶端的地址信息。

  • addrlen:是一個指向 socklen_t 類型的指針,用于指定 addr 緩沖區(qū)的長度,同時也用于返回實際客戶端地址結(jié)構(gòu)的大小。

  • accept函數(shù)作用是接受傳入的連接請求,他會阻塞程序的執(zhí)行,直到有一個連接請求到達。一旦有連接請求到達,將會創(chuàng)建一個新的套接字,并返回這個新套接字的文件描述符,這個新套接字用于與客戶端進行通信,同時addraddrlen會填充上客戶端的地址信息。

  • 在服務(wù)器程序中,accept函數(shù)會被用在一個循環(huán)中,以接受多個客戶端的連接請求

start函數(shù)作用是阻塞接受客戶端發(fā)送來的連接請求,使得服務(wù)器與客戶端建立通信

tcpclient.cc

#include<iostream>
#include<string>
#include<memory>
#include"tcpclient.hpp"
using namespace std;
using namespace client;
static void Usage(string proc)
{
    cout<<"\nUsage :\n\t"<<proc<<" serverip serverport\n"<<endl;
}
int main(int argc, char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(1);
    }

string serverip=argv[1];
uint16_t serverport=atoi(argv[2]);

unique_ptr<tcpclient> tc(new tcpclient(serverip,serverport));

tc->initclient();
tc->start();

    return 0;
}

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>
using namespace std;
#define NUM 1024
namespace client
{

    class tcpclient
{

public:
tcpclient(const string& ip,const uint16_t& port)
:_sock(-1)
,_port(port)
,_ip(ip)
{}

void initclient()
{
//1.創(chuàng)建sockfd
_sock=socket(AF_INET,SOCK_STREAM,0);
if(_sock<0)
{
   cerr<<"socket create error"<<endl;
   exit(2);
}
//2.綁定 ip port,不顯示綁定,OS自動綁定
}

void start()
{
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
socklen_t len=sizeof(ser);
ser.sin_family=AF_INET;
ser.sin_port=htons(_port);
ser.sin_addr.s_addr=inet_addr(_ip.c_str());
if(connect(_sock,(struct sockaddr *)&ser,len)!=0)
{
    cerr<<"connect error"<<endl;
}else
{
    string msg;
    while(true)
    {
        cout<<"Enter# ";
        getline(cin,msg);
        write(_sock,msg.c_str(),msg.size());
        
        char inbuffer[NUM];
        int n=read(_sock,inbuffer,sizeof(inbuffer)-1);
        if(n>0)
        {
            cout<<"server return :"<<inbuffer<<endl;
        }else
        {
            break;
        }
    }
}
}

~tcpclient()
{
    if(_sock>=0) close(_sock);
}

private:
int _sock;
uint16_t _port;
string _ip;

};
}

tcpserver.cc

#include"tcpserver.hpp"
#include"log.hpp"
#include<iostream>
#include<stdlib.h>
#include<memory>
using namespace Server;
using namespace std;

static void Usage(string proc)
{
    cout<<"\nUsage:\n\t"<<proc<<" local_port\n\n"<<endl;
}

int main(int argc,char* argv[])
{
if(argc!=2)
{
    Usage(argv[0]);
    exit(USAGE_ERR);
}

uint16_t port=atoi(argv[1]);//將字符串轉(zhuǎn)化為整數(shù)

unique_ptr<tcpserver> ts(new tcpserver(port));
ts->initserver();
ts->start();


return 0;
}

tcpserver.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include"log.hpp"
#define NUM 1024

using namespace std;
namespace Server
{
    enum
    {
        USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR
    };
class tcpserver;
    class ThreadData
    {
public:
ThreadData( tcpserver* self,int psock):_this(self),_psock(psock){}
tcpserver* _this;
int _psock;
    };

class tcpserver
{
 
public:

tcpserver(const  uint16_t& port):_port(port),_listensock(-1){}

void initserver()
{
//1.創(chuàng)建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
    logMessage(FATAL,"create listensocket error");
    exit(SOCK_ERR);
}
 logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//綁定失敗
{
    logMessage(FATAL,"bind error");
    exit(BIND_ERR);
}
 logMessage(NORMAL,"bind success");
//3.將套接字設(shè)置為監(jiān)聽模式
if(listen(_listensock,0)<0)
{
    logMessage(FATAL,"listen error");
    exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}

void start()
{
     // signal(SIGCHLD, SIG_IGN);
    threadPool<Task>::getthpptr()->run();
    while(true)
    {
        struct sockaddr_in cli;
        socklen_t len=sizeof(cli);
        bzero(&cli,len);

        int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
        if(sock<0)
        {
            logMessage(FATAL,"accept client error");
            continue;
        }
        logMessage(NORMAL,"accept client success");

        cout<<"accept sock: "<<sock<<endl;
        // serviceIO(sock);//客戶端串行版
        // close(sock);

        //多進程版---
        //一個客戶端占用一個文件描述符,原因在于孫子進程執(zhí)行IO任務(wù)需要占用獨立的文件描述符,而文件描述符是繼承父進程的,而每次客戶端進來都要占用新的文件描述符
        //因此若接收多個客戶端不退出的話文件描述符會越來越少。
//         pid_t id=fork();//創(chuàng)建子進程
//         if(id==0)//子進程進入
//         {
//             close(_listensock);//子進程不需要用于監(jiān)聽因此關(guān)閉該文件描述符
//             if(fork()>0)  exit(0);
// //子進程創(chuàng)建孫子進程,子進程直接退出,讓孫子進程擔任IO任務(wù),且孫子進程成為孤兒進程被OS領(lǐng)養(yǎng),
// //除非客戶端退出IO任務(wù)結(jié)束否則該孤兒進程一直運行下去不會相互干擾,即并行完成服務(wù)器和客戶端的通信

// //孫子進程
// serviceIO(sock);
// close(sock);
// exit(0);
//         }
//         //父進程
//         pid_t ret=waitpid(id,nullptr,0);
//         if(ret<0)
//         {
//             cout << "waitsuccess: " << ret << endl;
//         }

//多線程版
// pthread_t pid;
// ThreadData* th=new ThreadData(this,sock);
// pthread_create(&pid,nullptr,start_routine,th);

threadPool<Task>::getthpptr()->push(Task(sock,serviceIO));
    }
}

// static void* start_routine(void* args)
// {
//     pthread_detach(pthread_self());
//     ThreadData* ret=static_cast<ThreadData*>(args);
//     ret->_this->serviceIO(ret->_psock);
//     close(ret->_psock);
//     delete ret;
//     return nullptr;
// } 

// void serviceIO(int sock)
// {
//     char inbuffer[NUM];
//     while(true)
//     {
//         ssize_t n=read(sock,inbuffer,sizeof(inbuffer)-1);
//         if(n>0)
//         {
//             inbuffer[n]=0;
//             cout<<"recv message: "<<inbuffer<<endl;
//             string outb=inbuffer;
//             string outbuffer=outb+"[server echo]";

//             write(sock,outbuffer.c_str(),outbuffer.size());

//         }
// else
// {
//     logMessage(NORMAL,"client quit,i quit yep");
//     break;
// }
//     }

// }

~tcpserver(){}

private:
int _listensock;//用于監(jiān)聽服務(wù)器的sock文件描述符
uint16_t _port;//端口號
};

}
1. 單進程版:客戶端串行版
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include"log.hpp"
#define NUM 1024

using namespace std;
namespace Server
{
    enum
    {
        USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR
    };

class tcpserver
{
 
public:

tcpserver(const  uint16_t& port):_port(port),_listensock(-1){}

void initserver()
{
//1.創(chuàng)建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
    logMessage(FATAL,"create listensocket error");
    exit(SOCK_ERR);
}
 logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//綁定失敗
{
    logMessage(FATAL,"bind error");
    exit(BIND_ERR);
}
 logMessage(NORMAL,"bind success");
//3.將套接字設(shè)置為監(jiān)聽模式
if(listen(_listensock,0)<0)
{
    logMessage(FATAL,"listen error");
    exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}

void start()
{
    while(true)
    {
        struct sockaddr_in cli;
        socklen_t len=sizeof(cli);
        bzero(&cli,len);

        int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
        if(sock<0)
        {
            logMessage(FATAL,"accept client error");
            continue;
        }
        logMessage(NORMAL,"accept client success");

        cout<<"accept sock: "<<sock<<endl;
         serviceIO(sock);//客戶端串行版
         close(sock);
    }
}


void serviceIO(int sock)
{
    char inbuffer[NUM];
    while(true)
    {
        ssize_t n=read(sock,inbuffer,sizeof(inbuffer)-1);
        if(n>0)
        {
            inbuffer[n]=0;
            cout<<"recv message: "<<inbuffer<<endl;
            string outb=inbuffer;
            string outbuffer=outb+"[server echo]";

            write(sock,outbuffer.c_str(),outbuffer.size());

        }
else
{
    logMessage(NORMAL,"client quit,i quit yep");
    break;
}
    }

}

~tcpserver(){}

private:
int _listensock;//用于監(jiān)聽服務(wù)器的sock文件描述符
uint16_t _port;//端口號
};

}

注意:客戶端串行給服務(wù)器發(fā)數(shù)據(jù)是在哪里堵塞?由于阻塞在accept函數(shù)處,即accept等待客戶端接入是阻塞式等待。accept函數(shù)接收了一個連接請求后,后來的客戶端連接請求需要在accept函數(shù)處等待,當上一個客戶端退出后,服務(wù)器才能accept當前客戶端發(fā)送來的連接請求成功,才能接收當前客戶端的數(shù)據(jù)。即服務(wù)器串行接收處理客戶端發(fā)送來的數(shù)據(jù)

2. 多進程版:客戶端并行版

tcpserver.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include"log.hpp"
#define NUM 1024

using namespace std;
namespace Server
{
    enum
    {
        USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR
    };
class tcpserver
{
 
public:

tcpserver(const  uint16_t& port):_port(port),_listensock(-1){}

void initserver()
{
//1.創(chuàng)建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
    logMessage(FATAL,"create listensocket error");
    exit(SOCK_ERR);
}
 logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//綁定失敗
{
    logMessage(FATAL,"bind error");
    exit(BIND_ERR);
}
 logMessage(NORMAL,"bind success");
//3.將套接字設(shè)置為監(jiān)聽模式
if(listen(_listensock,0)<0)
{
    logMessage(FATAL,"listen error");
    exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}

void start()
{
     // signal(SIGCHLD, SIG_IGN);
    while(true)
    {
        struct sockaddr_in cli;
        socklen_t len=sizeof(cli);
        bzero(&cli,len);

        int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
        if(sock<0)
        {
            logMessage(FATAL,"accept client error");
            continue;
        }
        logMessage(NORMAL,"accept client success");

        cout<<"accept sock: "<<sock<<endl;

                //多進程版---
        //一個客戶端占用一個文件描述符,原因在于孫子進程執(zhí)行IO任務(wù)需要占用獨立的文件描述符,而文件描述符是繼承父進程的,而每次客戶端進來都要占用新的文件描述符
        //因此若接收多個客戶端不退出的話文件描述符會越來越少。
        pid_t id=fork();//創(chuàng)建子進程
        if(id==0)//子進程進入
        {
            close(_listensock);//子進程不需要用于監(jiān)聽因此關(guān)閉該文件描述符
            if(fork()>0)  exit(0);
// //子進程創(chuàng)建孫子進程,子進程直接退出,讓孫子進程擔任IO任務(wù),且孫子進程成為孤兒進程被OS領(lǐng)養(yǎng),
// //除非客戶端退出IO任務(wù)結(jié)束否則該孤兒進程一直運行下去不會相互干擾,即并行完成服務(wù)器和客戶端的通信

// //孫子進程
serviceIO(sock);
close(sock);
exit(0);
        }
        //父進程
         // close(sock);//父進程不使用文件描述符就關(guān)閉
        pid_t ret=waitpid(id,nullptr,0);
        if(ret<0)
        {
            cout << "waitsuccess: " << ret << endl;
        }
    }
}
void serviceIO(int sock)
{
    char inbuffer[NUM];
    while(true)
    {
        ssize_t n=read(sock,inbuffer,sizeof(inbuffer)-1);
        if(n>0)
        {
            inbuffer[n]=0;
            cout<<"recv message: "<<inbuffer<<endl;
            string outb=inbuffer;
            string outbuffer=outb+"[server echo]";

            write(sock,outbuffer.c_str(),outbuffer.size());

        }
else
{
    logMessage(NORMAL,"client quit,i quit yep");
    break;
}
    }

}
~tcpserver(){}
private:
int _listensock;//用于監(jiān)聽服務(wù)器的sock文件描述符
uint16_t _port;//端口號
};

}
  • 父進程fork創(chuàng)建子進程,創(chuàng)建完后waitpid等待回收子進程。子進程fork創(chuàng)建孫子進程,創(chuàng)建完后直接退出。導(dǎo)致孫子進程成為孤兒進程,進而被OS領(lǐng)養(yǎng)。因此除非客戶端退出IO任務(wù),否則孤兒進程將一直運行下去不會干擾到其他進程,即并行完成服務(wù)器和客戶端的通信

  • 注意的是服務(wù)器accept一次客戶端的連接請求,就需要申請一個文件描述符,而文件描述符是有上限的,如果大量的客戶端請求連接成功并且不結(jié)束的話,會造成文件描述符泄露。

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

因此在父進程那里需要關(guān)閉不使用的文件描述符

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

  • 父進程這里回收子進程,不能使用非阻塞等待,原因在于非阻塞等待的本質(zhì)是輪詢,而這里使用后會導(dǎo)致父進程會在accept函數(shù)處阻塞等待客戶端發(fā)送連接請求,那么父進程就無法回收子進程了。因此waitpid的返回值用ret接收,等待回收成功就打印日志,失敗則跳過
  • 當子進程圖退出或者被中止時子進程會發(fā)送17號信號SIGCHILD給父進程,父進程可以通過忽略17號信號SIGCHILD的方式來不阻塞等待回收子進程(這種方法對于linux環(huán)境可用,其余不保證)
signal(SIGCHLD, SIG_IGN);
netstat查看網(wǎng)絡(luò)信息

netstat 是一個用于查看網(wǎng)絡(luò)連接和網(wǎng)絡(luò)統(tǒng)計信息的命令行工具。它可以用來顯示當前系統(tǒng)上的網(wǎng)絡(luò)連接、路由表、接口統(tǒng)計信息等等。在 Linux 系統(tǒng)中,netstat 命令的用法如下:

netstat [options]

一些常用的選項包括:

  • -a:顯示所有的連接,包括監(jiān)聽中和已建立的連接。
  • -t:顯示 TCP 協(xié)議的連接。
  • -u:顯示 UDP 協(xié)議的連接。
  • -n:以數(shù)字形式顯示 IP 地址和端口號,而不是嘗試進行 DNS 解析。
  • -p:顯示與連接關(guān)聯(lián)的進程信息。
  • -r:顯示路由表。
  • -l:僅顯示監(jiān)聽中的連接。
  • -atun:顯示所有的TCP和UDP連接

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

注意一下:這里出現(xiàn)了兩個連接,原因在于服務(wù)器和客戶端在同一臺主機上,即服務(wù)器和客戶端完成了本地環(huán)回,因此能看到兩個連接。

3.多線程版:并行執(zhí)行

tcpserver.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include"log.hpp"
#define NUM 1024

using namespace std;
namespace Server
{
    enum
    {
        USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR
    };
class tcpserver;
    class ThreadData
    {
public:
ThreadData( tcpserver* self,int psock):_this(self),_psock(psock){}
tcpserver* _this;
int _psock;
    };

class tcpserver
{
 
public:

tcpserver(const  uint16_t& port):_port(port),_listensock(-1){}

void initserver()
{
//1.創(chuàng)建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{
    logMessage(FATAL,"create listensocket error");
    exit(SOCK_ERR);
}
 logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//綁定失敗
{
    logMessage(FATAL,"bind error");
    exit(BIND_ERR);
}
 logMessage(NORMAL,"bind success");
//3.將套接字設(shè)置為監(jiān)聽模式
if(listen(_listensock,0)<0)
{
    logMessage(FATAL,"listen error");
    exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}

void start()
{
    while(true)
    {
        struct sockaddr_in cli;
        socklen_t len=sizeof(cli);
        bzero(&cli,len);

        int sock=accept(_listensock,(struct sockaddr*)&cli,&len);
        if(sock<0)
        {
            logMessage(FATAL,"accept client error");
            continue;
        }
        logMessage(NORMAL,"accept client success");

        cout<<"accept sock: "<<sock<<endl;
        //多線程版
pthread_t pid;
ThreadData* th=new ThreadData(this,sock);
pthread_create(&pid,nullptr,start_routine,th);
    }
}
 static void* start_routine(void* args)
{
    pthread_detach(pthread_self());//線程分離后讓OS自動回收新線程
    ThreadData* ret=static_cast<ThreadData*>(args);
    ret->_this->serviceIO(ret->_psock);
    close(ret->_psock);
    delete ret;
    return nullptr;
}    
    
void serviceIO(int sock)
{
    char inbuffer[NUM];
    while(true)
    {
        ssize_t n=read(sock,inbuffer,sizeof(inbuffer)-1);
        if(n>0)
        {
            inbuffer[n]=0;
            cout<<"recv message: "<<inbuffer<<endl;
            string outb=inbuffer;
            string outbuffer=outb+"[server echo]";

            write(sock,outbuffer.c_str(),outbuffer.size());

        }
else
{
    logMessage(NORMAL,"client quit,i quit yep");
    break;
}
    }

}
~tcpserver(){}
private:
int _listensock;//用于監(jiān)聽服務(wù)器的sock文件描述符
uint16_t _port;//端口號
};

}
  • 服務(wù)器接收一個客戶端的連接請求,就申請一個新線程,多線程下可以讓服務(wù)器接收多個線程
log.hpp
#pragma once

#include <iostream>
#include <string>
#include<ctime>
#include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
#include <stdarg.h>
using namespace std;
#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4

#define NUM 1024
#define LOG_STR "./logstr.txt"
#define LOG_ERR "./log.err"
const char* to_str(int level)
{
    switch(level)
    {
        case DEBUG: return "DEBUG";
        case NORMAL: return "NORMAL";
        case WARNING: return "WARNING";
        case ERROR: return "ERROR";
        case FATAL: return "FATAL";
        default: return nullptr;
    }
}

void logMessage(int level, const char* format,...)
{
    // [日志等級] [時間戳/時間] [pid] [messge]
    // [WARNING] [2023-05-11 18:09:08] [123] [創(chuàng)建socket失敗]

    // 暫定
  //  std::cout << message << std::endl;

char logprestr[NUM];
snprintf(logprestr,sizeof(logprestr),"[%s][%ld][%d]",to_str(level),(long int)time(nullptr),getpid());//把后面的內(nèi)容打印進logprestr緩存區(qū)中

char logeldstr[NUM];
va_list arg;
va_start(arg,format); 
vsnprintf(logeldstr,sizeof(logeldstr),format,arg);//arg是logmessage函數(shù)列表中的...

  cout<<logprestr<<logeldstr<<endl;

//  FILE* str=fopen(LOG_STR,"a");
//  FILE* err=fopen(LOG_ERR,"a");//以追加方式打開文件,若文件不存在則創(chuàng)建
 
//  if(str!=nullptr||err!=nullptr)//兩個文件指針都不為空則創(chuàng)建文件成功
//  {
//   FILE* ptr=nullptr;
//   if(level==DEBUG||level==NORMAL||level==WARNING)
//   {
//     ptr=str;
//   }
//    if(level==ERROR||level==FATAL)
//   {
//     ptr=err;
//   }
//   if(ptr!=nullptr)
//   {
//     fprintf(ptr,"%s-%s\n",logprestr,logeldstr);
//   }
//   fclose(str);
//   fclose(err);
 //}

}

可變參數(shù)列表

  • va_list是(char*)重命名的類型,定義可以訪問可變參數(shù)的變量。

  • va_start(ap, v) ap是定義的可變參數(shù)變量,v是形參中可變參數(shù)前第一個參數(shù)名,其作用是使ap指向可變參數(shù)部分。

  • va_arg(ap, t) ap是定義的可變參數(shù)變量,t是可變參數(shù)的類型,根據(jù)類型,訪問可變參數(shù)列表中的數(shù)據(jù)。

  • va_end(ap) ap是定義的可變參數(shù)變量,使ap變量置空,作為結(jié)束使用。

vsnprintf函數(shù)原型

#include <stdio.h>
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
  • str是一個指向字符數(shù)組(緩沖區(qū))的指針,用于存儲格式化后的數(shù)據(jù)

  • size是緩沖區(qū)的大小,限制了寫入的最大字符數(shù),包括終止的 null 字符

  • format格式化字符串,類似于 printf 函數(shù)中的格式化字符串

  • ap是一個 va_list 類型的變量,用于存儲可變參數(shù)列表的信息,并且要注意OS對參數(shù)壓棧的順序是從右向左

  • vsnprintf 函數(shù)根據(jù)指定的 format 格式化字符串將數(shù)據(jù)寫入 str 緩沖區(qū),但不會超出指定的緩沖區(qū)大小。它會在寫入數(shù)據(jù)后自動在緩沖區(qū)末尾添加一個 null 終止字符,確保結(jié)果是一個合法的 C 字符串。

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

守護進程

守護進程(Daemon)是在計算機系統(tǒng)中以后臺方式運行的一類特殊進程。它通常在操作系統(tǒng)啟動時被初始化,并在整個系統(tǒng)運行期間保持活動狀態(tài),不需要與用戶交互。守護進程通常用于執(zhí)行系統(tǒng)任務(wù)、服務(wù)管理以及提供后臺服務(wù),如網(wǎng)絡(luò)服務(wù)、定時任務(wù)等。

守護進程特點如下:

  1. 后臺運行,守護進程在后臺運行,不與用戶交互,沒有控制終端。
  2. 獨立性:它通常獨立于用戶會話,即使用戶注銷或關(guān)閉終端,守護進程也會繼續(xù)運行。
  3. 沒有標準輸入輸出:守護進程通常沒有標準輸入和輸出,因為它們不與用戶交互。它們通常將輸出寫入日志文件。
  4. 分離自身:守護進程會通過一系列操作來與終端、會話和控制組脫離連接,以確保它不會意外地被控制終端關(guān)閉。

一個服務(wù)器中可以具有多個會話,例如一個服務(wù)器上有一個root用戶和多個普通用戶,當普通用戶登錄上服務(wù)器時即成為一個會話。

一個會話具有多個后臺任務(wù),但只能具有一個前臺任務(wù)(bash)。

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

  • jobs查看任務(wù)可以看到任務(wù)1是./tcpserver,任務(wù)2是sleep 1000 | sleep 2000 | sleep 3000 &,任務(wù)3是sleep 4000 | sleep 5000 &,且三個任務(wù)后面都帶&,在進程或任務(wù)后帶&作用是將該任務(wù)放到后臺運行
  • sleep 1000 、sleep 2000 、sleep 3000 、sleep 4000、sleep 5000的父進程都是16853即bash;而 sleep 1000 、sleep 2000 、sleep 3000的PGID相同,都是sleep 1000的pid,即 sleep 1000 、sleep 2000 、sleep 3000屬于同一組,同一個組要協(xié)同起來完成同一個作業(yè)。第一個任務(wù)的pid是組長的pid即sleep 1000的pid;而小組16858和小組17070的SID都是16853,即這兩個小組屬于同一個會話(bash),要完成的是同一個任務(wù);
fg、bg
  1. fg 作業(yè)號:將作業(yè)放到前臺

  2. bg 作業(yè)號:將作業(yè)放到后臺,或者繼續(xù)執(zhí)行后臺作業(yè)

  3. ctrl+Z將前臺任務(wù)暫停并把作業(yè)放到后臺

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

  • 用戶登錄時服務(wù)器就需要為此創(chuàng)建一些后臺作業(yè)和前臺作業(yè)(命令行)來服務(wù)用戶,而用戶注銷或退出服務(wù)器也會影響其前臺作業(yè)和后臺作業(yè)。而服務(wù)器程序不能受到用戶登錄和注銷的影響。
  • 我們可以使得服務(wù)器程序自成會話,自成進程組,那么該程序就與終端設(shè)備無關(guān),不能再收到用戶登錄和注銷的影響了。該類進程被稱為守護進程
setsid

在Unix和類Unix系統(tǒng)中,setsid 是一個用于創(chuàng)建新會話的系統(tǒng)調(diào)用函數(shù)。會話(Session)是一組相關(guān)的進程組合,通常由一個控制終端和一些子進程組成。setsid 函數(shù)的主要作用是將調(diào)用它的進程從當前會話中分離出來,并創(chuàng)建一個新的會話。

 #include <unistd.h>
pid_t setsid(void);
  • 創(chuàng)建新會話:調(diào)用 setsid 的進程會成為一個新的會話的組長(Session Leader)。新會話不再與之前的控制終端相關(guān)聯(lián)。但該進程在調(diào)用setsid函數(shù)之前不能是組長。
  • 分離終端:調(diào)用 setsid 的進程不再與任何控制終端關(guān)聯(lián),無法重新獲得控制終端。
  • 成為新進程組的組長:新會話中的第一個進程(調(diào)用 setsid 的進程)會成為新的進程組的組長。

daemon.hpp

#pragma once

#include <unistd.h>
#include <signal.h>
#include <cstdlib>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define DEV "/dev/null"
void daemonSelf(const char *currPath = nullptr)
{
    // 1. 讓調(diào)用進程忽略掉異常的信號
signal(SIGPIPE,SIG_IGN);//選擇忽略SIGPIPE信號
    // 2. 如何讓自己不是組長,setsid
if(fork()>0)
exit(0);//父進程退出
    // 子進程 -- 守護進程,精靈進程,本質(zhì)就是孤兒進程的一種!
pid_t ret=setsid();
assert(ret!=-1);
    // 3. 守護進程是脫離終端的,關(guān)閉或者重定向以前進程默認打開的文件
int fd=open(DEV,O_RDWR);
if(fd>=0)
{
    //dup2(oldfd,newfd):將oldfd的內(nèi)容填充到newfd中,這樣輸入到newfd的內(nèi)容被重定向到oldfd
    dup2(fd,0);
    dup2(fd,1);
    dup2(fd,2);
}else
{
    close(0);
    close(1);
    close(2);
}
    // 4. 可選:進程執(zhí)行路徑發(fā)生更改
if(currPath) chdir(currPath);//更改currPath的路徑
}
  • /dev/null 是一個特殊的設(shè)備文件,它被用作數(shù)據(jù)丟棄點,向它寫入的數(shù)據(jù)會被丟棄,從它讀取數(shù)據(jù)會立即返回EOF(End of File)
  • SIGPIPE的觸發(fā)場景:當一個進程向一個已經(jīng)關(guān)閉寫端的管道(或者套接字)寫數(shù)據(jù)時、當進程向一個已經(jīng)收到 RST 包(連接重置)的套接字發(fā)送數(shù)據(jù)時,該進程就會向父進程發(fā)送SIGPIPE信號來進行進程終止。對SIGPIPE進行忽略行為避免了進程向/dev/null中寫入數(shù)據(jù)并出現(xiàn)錯誤導(dǎo)致的進程終止
  • 父進程創(chuàng)建子進程,父進程作為組長,父進程退出后,子進程能夠自己成為組長即能夠成為守護進程
  • dup2(oldfd,newfd):將oldfd的內(nèi)容填充到newfd中,這樣輸入到newfd的內(nèi)容被重定向到oldfd。在代碼中是將輸入文件描述符012的內(nèi)容重定向到fd即/dev/null中

tcpserver.cc

#include"tcpserver.hpp"
#include"log.hpp"
#include"daemon.hpp"
#include<iostream>
#include<stdlib.h>
#include<memory>
using namespace Server;
using namespace std;

static void Usage(string proc)
{
    cout<<"\nUsage:\n\t"<<proc<<" local_port\n\n"<<endl;
}

int main(int argc,char* argv[])
{
if(argc!=2)
{
    Usage(argv[0]);
    exit(USAGE_ERR);
}

uint16_t port=atoi(argv[1]);//將字符串轉(zhuǎn)化為整數(shù)

unique_ptr<tcpserver> ts(new tcpserver(port));
ts->initserver();
daemonSelf();
ts->start();

return 0;
}

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

TCP協(xié)議通信流程

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

首先需要服務(wù)器初始化

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

服務(wù)器初始化:

  • 調(diào)用socket, 創(chuàng)建文件描述符,該文件描述符用于監(jiān)聽;
  • 調(diào)用bind, 將當前的文件描述符和ip/port綁定在一起; 如果這個端口已經(jīng)被其他進程占用了, 就會bind失敗;
  • 調(diào)用listen, 聲明當前這個文件描述符作為一個服務(wù)器的文件描述符, 該文件描述符處于監(jiān)聽狀態(tài),等待客戶端發(fā)起連接;
  • 調(diào)用accecpt, 并阻塞, 等待客戶端連接過來;
建立連接的過程(通常稱為三次握手)

建立連接的過程(內(nèi)含三次握手)

  • 客戶端調(diào)用socket,創(chuàng)建文件描述符;
  • 客戶端調(diào)用connect,向指定地址端口的服務(wù)器發(fā)起請求;(請求的過程中會進行三次握手)
  • connect會發(fā)出SYN段給服務(wù)器并阻塞等待服務(wù)器應(yīng)答;(第一次握手)
  • 服務(wù)器收到客戶端的SYN段后,會應(yīng)答一個SYN-ACK段表示"同意建立連接";(第二次握手)
  • 客戶端收到SYN-ACK后,會從connet()返回,同時發(fā)送一個應(yīng)答ACK段給服務(wù)器;(第三次握手)
  • 服務(wù)器收到客戶端發(fā)來的ACK段后,會從accpet()返回,返回(分配)一個新的文件描述符connfd用于與客戶端通信
  • 可以看到三次握手由connect()發(fā)起請求開始,并由connect()返回結(jié)束,因此客戶端在調(diào)用connect()時本質(zhì)就是通過某種方式與服務(wù)器進行三次握手

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

**對于建鏈接的3次握手,**主要是要初始化Sequence Number 的初始值。通信的雙方要互相通知對方自己的初始化的Sequence Number(縮寫為ISN:Inital Sequence Number)——所以叫SYN,全稱Synchronize Sequence Numbers。也就上圖中的 x 和 y。這個號要作為以后的數(shù)據(jù)通信的序號,以保證應(yīng)用層接收到的數(shù)據(jù)不會因為網(wǎng)絡(luò)上的傳輸?shù)膯栴}而亂序(TCP會用這個序號來拼接數(shù)據(jù))。來自陳浩大佬對于三次握手的部分詮釋

  • 連接建立成功后會被accpet獲取到,此時客戶端和服務(wù)器就能進行通信了。要注意的是,連接建立是三次握手做的事,三次握手是TCP底層的工作,而accep要做的是把底層已經(jīng)建立好的連接拿到用戶層,即accept本身不參于三次握手這個過程(不參與建立連接),accpet會阻塞等待獲取建立好的連接,若連接沒有建立好會進行等待。
數(shù)據(jù)傳輸?shù)倪^程

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

  • 建立連接后,TCP協(xié)議提供全雙工的通信服務(wù); 所謂全雙工的意思是, 在同一條連接中, 同一時刻, 通信雙方 可以同時寫數(shù)據(jù); 其原因在于服務(wù)器和客戶端的應(yīng)用層和傳輸層都有兩個緩沖區(qū),一個是發(fā)送緩沖區(qū)另一個是接收緩沖區(qū),那么服務(wù)器和客戶端進行發(fā)送和讀取并不會互相影響。相對的概念叫做半雙工, 同一條連接在同一時刻, 只能由一方來寫數(shù)據(jù);

  • 服務(wù)器從accept()返回后立刻調(diào) 用read(), 讀socket就像讀管道一樣, 如果沒有數(shù)據(jù)到達就阻塞等待;

  • 這時客戶端調(diào)用write()發(fā)送請求給服務(wù)器, 服務(wù)器收到后從read()返回,對客戶端的請求進行處理, 在此期 間客戶端調(diào)用read()阻塞等待服務(wù)器的應(yīng)答;

  • 服務(wù)器調(diào)用write()將處理結(jié)果發(fā)回給客戶端, 再次調(diào)用read()阻塞等待下一條請求;

  • 客戶端收到后從read()返回, 發(fā)送下一條請求,如此循環(huán)下去

斷開連接的過程

網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器,網(wǎng)絡(luò),網(wǎng)絡(luò),tcp/ip,服務(wù)器

  • 如果客戶端沒有更多的請求了, 就調(diào)用close()關(guān)閉連接, 客戶端會向服務(wù)器發(fā)送FIN段;(第一次握手)
  • 此時服務(wù)器收到FIN后, 會回應(yīng)一個ACK, 同時read會返回0 ;(第二次握手)
  • read返回之后, 服務(wù)器就知道客戶端關(guān)閉了連接, 也調(diào)用close關(guān)閉連接, 這個時候服務(wù)器會向客戶端發(fā)送 一個FIN; (第三次握手)
  • 客戶端收到FIN, 再返回一個ACK給服務(wù)器; (第四次握手)
  • 這個斷開連接的過程, 通常稱為四次揮手

  • 對于4次揮手其實你仔細看是2次,因為TCP是全雙工的,所以,發(fā)送方和接收方都需要Fin和Ack。只不過,有一方是被動的,所以看上去就成了所謂的4次揮手。如果兩邊同時斷連接,那就會就進入到CLOSING狀態(tài),然后到達TIME_WAIT狀態(tài)。

當客戶端不與服務(wù)器通信時需要斷開連接的原因文章來源地址http://www.zghlxwxcb.cn/news/detail-683136.html

  • 其實,網(wǎng)絡(luò)上的傳輸是沒有連接的,包括TCP也是一樣的。而TCP所謂的“連接”,其實只不過是在通訊的雙方維護一個“連接狀態(tài)”,讓它看上去好像有連接一樣。所以,TCP的狀態(tài)變換是非常重要的。若通信結(jié)束不及時斷開連接,即占用著操作系統(tǒng)的資源不使用,會導(dǎo)致系統(tǒng)的資源越來越少。
  • 服務(wù)器能夠與多個客戶端建立連接,意味著服務(wù)器會收到大量的連接,因此操作系統(tǒng)要對這些連接進行管理,即"先組織再管理",在服務(wù)端就需要維護連接相關(guān)的數(shù)據(jù)結(jié)構(gòu),把這些數(shù)據(jù)結(jié)構(gòu)組織起來,那么對連接的管理轉(zhuǎn)變?yōu)閷?shù)據(jù)結(jié)構(gòu)的管理。
  • 操作系統(tǒng)需要維護這些連接相關(guān)的數(shù)據(jù)結(jié)構(gòu),勢必需要消耗資源,而不通信的連接不斷開,會導(dǎo)致操作系統(tǒng)的資源浪費。而TCP與UDP的區(qū)別之一在于TCP需要對連接相關(guān)的資源進行管理。

到了這里,關(guān)于網(wǎng)絡(luò)字節(jié)序——TCP接口及其實現(xiàn)簡單TCP服務(wù)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 簡單的TCP網(wǎng)絡(luò)程序·線程池(后端服務(wù)器)

    簡單的TCP網(wǎng)絡(luò)程序·線程池(后端服務(wù)器)

    目錄 版本四:線程池 注意事項 文件:Task.hpp -- 任務(wù)單獨為一個文件 組件:日志修改 新函數(shù):vprintf() 可變參數(shù)的提取邏輯 vfprintf()的工作原理 初始化一個va_list 日志準備 獲取時間小知識 日志初版 日志啟動測試 TCP通用服務(wù)器(守護進程)?* 新指令1:jobs -- 查看進程作業(yè) 新指令

    2024年02月09日
    瀏覽(26)
  • 【網(wǎng)絡(luò)原理】使用Java基于TCP搭建簡單客戶端與服務(wù)器通信

    【網(wǎng)絡(luò)原理】使用Java基于TCP搭建簡單客戶端與服務(wù)器通信

    TCP服務(wù)器與客戶端的搭建需要借助以下API ServerSocket 是創(chuàng)建TCP服務(wù)端Socket的API。 ServerSocket 構(gòu)造方法 : 方法簽名 方法說明 ServerSocket(int port) 創(chuàng)建一個服務(wù)端流套接字Socket,并綁定到指定端口 ServerSocket 方法: 方法簽名 方法說明 Socket accept() 開始監(jiān)聽指定端口(創(chuàng)建時綁定的端

    2024年03月12日
    瀏覽(34)
  • Socket編程接口API并實現(xiàn)簡單的TCP網(wǎng)絡(luò)編程

    Socket編程接口API并實現(xiàn)簡單的TCP網(wǎng)絡(luò)編程

    #include sys/types.h #include sys/socket.h socket()創(chuàng)建套接字,成功返回套接字的文件描述符,失敗返回-1 domain: 設(shè)置套接字的協(xié)議簇, AF_UNIX AF_INET AF_INET6 type: 設(shè)置套接字的服務(wù)類型 SOCK_STREAM SOCK_DGRAM protocol: 一般設(shè)置為 0,表示使用默認協(xié)議 int socket(int domain, int type, int protocol); bind()將

    2024年02月13日
    瀏覽(31)
  • C#網(wǎng)絡(luò)TCP服務(wù)器端的實現(xiàn)

    C#網(wǎng)絡(luò)TCP服務(wù)器端的實現(xiàn)

    1、實現(xiàn)代碼 2、運行效果

    2024年02月11日
    瀏覽(22)
  • 【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)

    【網(wǎng)絡(luò)編程】demo版TCP網(wǎng)絡(luò)服務(wù)器實現(xiàn)

    UDP和TCP的區(qū)別: 對于TCP協(xié)議有幾個特點: 1?? 傳輸層協(xié)議 2?? 有連接(正式通信前要先建立連接) 3?? 可靠傳輸(在內(nèi)部幫我們做可靠傳輸工作) 4?? 面向字節(jié)流 對于UDP協(xié)議有幾個特點: 1?? 傳輸層協(xié)議 2?? 無連接 3?? 不可靠傳輸 4?? 面向數(shù)據(jù)報 可以看到

    2024年02月06日
    瀏覽(29)
  • 計算機網(wǎng)絡(luò)套接字編程實驗-TCP單進程循環(huán)服務(wù)器程序與單進程客戶端程序(簡單回聲)

    1.實驗系列 ·Linux NAP-Linux網(wǎng)絡(luò)應(yīng)用編程系列 2.實驗?zāi)康?·理解并掌握在程序運行時從命令行讀取數(shù)據(jù)的C語言編程方法; ·理解并掌握基于命令參數(shù)設(shè)置并獲取IP與Port的C語言編程方法; ·理解并掌握套接字地址的數(shù)據(jù)結(jié)構(gòu)定義與地址轉(zhuǎn)換函數(shù)應(yīng)用; ·理解并掌握網(wǎng)絡(luò)字節(jié)序

    2024年02月11日
    瀏覽(43)
  • 計算機網(wǎng)絡(luò)套接字編程實驗-TCP多進程并發(fā)服務(wù)器程序與單進程客戶端程序(簡單回聲)

    1.實驗系列 ·Linux NAP-Linux網(wǎng)絡(luò)應(yīng)用編程系列 2.實驗?zāi)康?·理解多進程(Multiprocess)相關(guān)基本概念,理解父子進程之間的關(guān)系與差異,熟練掌握基于fork()的多進程編程模式; ·理解僵尸進程產(chǎn)生原理,能基于|sigaction()或signal(),使用waitpid()規(guī)避僵尸進程產(chǎn)生; ·

    2024年02月12日
    瀏覽(37)
  • 【網(wǎng)絡(luò)編程】TCP流套接字編程(TCP實現(xiàn)回顯服務(wù)器)

    【網(wǎng)絡(luò)編程】TCP流套接字編程(TCP實現(xiàn)回顯服務(wù)器)

    Socket(既能給客戶端使用,也能給服務(wù)器使用) 構(gòu)造方法 基本方法: ServerSocket(只能給服務(wù)器使用) 構(gòu)造方法: 基本方法: 客戶端代碼示例: 服務(wù)器代碼示例: 運行結(jié)果: 代碼執(zhí)行流程: 服務(wù)器啟動,阻塞在accept,等待客戶端建立連接. 客戶端啟動.這里的new操作會觸發(fā)和服務(wù)器之間建立連

    2024年04月25日
    瀏覽(23)
  • 【網(wǎng)絡(luò)編程】實現(xiàn)UDP/TCP客戶端、服務(wù)器

    【網(wǎng)絡(luò)編程】實現(xiàn)UDP/TCP客戶端、服務(wù)器

    需要云服務(wù)器等云產(chǎn)品來學習Linux的同學可以移步/--騰訊云--/--阿里云--/--華為云--/官網(wǎng),輕量型云服務(wù)器低至112元/年,新用戶首次下單享超低折扣。 ? 目錄 一、UDP 1、Linux客戶端、服務(wù)器 1.1udpServer.hpp 1.2udpServer.cc 1.3udpClient.hpp 1.4udpClient.cc 1.5onlineUser.hpp 2、Windows客戶端 二、T

    2024年02月06日
    瀏覽(19)
  • 【網(wǎng)絡(luò)】UDP網(wǎng)絡(luò)服務(wù)器簡單模擬實現(xiàn)

    【網(wǎng)絡(luò)】UDP網(wǎng)絡(luò)服務(wù)器簡單模擬實現(xiàn)

    【網(wǎng)絡(luò)】UDP網(wǎng)絡(luò)服務(wù)器簡單模擬實現(xiàn) UDP的封裝 : UDP網(wǎng)絡(luò)服務(wù)器模擬實現(xiàn):主要分為makefile文件進行編譯 UDP客戶端 :udpClient.cc(客戶端的調(diào)用),udpClient.hpp(客戶端的實現(xiàn)) UDP服務(wù)端 :udpServer.cc(服務(wù)端的調(diào)用),udpServer.hpp(服務(wù)端的實現(xiàn)) 創(chuàng)建makefile文件: makefile里可以定義變

    2024年02月08日
    瀏覽(31)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包