yo!這里是socket網絡編程相關介紹

目錄

前言

基本概念

源ip&&目的ip

源端口號&&目的端口號

udp&&tcp初識?

socket編程?

網絡字節序

socket常見接口

socket

bind

listen

accept

connect

地址轉換函數

字符串轉in_addr

in_addr轉字符串

套接字讀寫函數

recvfrom&&recv

sendto&&send

簡單udp服務器客戶端程序

udp服務器

udp客戶端

簡單tcp服務器客戶端程序

接口理解

tcp服務器?

tcp客戶端

后記


前言

? ? ? ? 在學習網絡部分知識時,只學習或只背誦概念是沒有用的,面試過程中面試官肯定會究其細節或理解從各方面考察,因此上章節介紹完網絡初步相關概念之后,我們先上代碼去直觀地了解網絡,為后面更深層次或者復雜的概念或網絡傳輸過程的細節打下基礎。那么,在網絡部分socket編程就是用于在不同計算機之間進行通信,因此本章節就是介紹socket編程相關接口以及運用其編寫簡單的程序去了解通信過程。

基本概念

  • 源ip&&目的ip

? ? ? ? 在ip數據包的頭部中存在兩個IP地址——源ip&&目的ip,即此數據包從哪來&&去哪里。我們知道,IP地址(此處說的是公網ip),標定了主機的唯一性,但通過IP地址將數據發送到主機并不是完整的網絡通信過程,也就是說一臺主機的信息到達了目標主機上真的就算是數據發送成功了嗎?并不是,其實網絡通信過程的本質就是進程間通信(由一臺主機的進程發送到另一臺主機的進程),將數據在主機間轉發只是手段,主機收到后需要將數據交付給相應進程。但是,數據到達目標主機后該走向哪個進程呢?這就需要下一個概念——端口號了。

  • 源端口號&&目的端口號

? ? ? ? 端口號(2字節16位的整數)傳輸層的內容,標識特定主機上網絡進程的唯一性,也就是標識一個進程,告訴os數據交給哪個進程來處理。可見,一個端口號只能被一個進程占用,但一個進程可以綁定多個端口號

? ? ? ??源端口號&&目的端口號存在于傳輸層的數據段中,標識哪個進程發的&&發給哪個進程。綜上ip+端口號表示指定主機的指定進程,這是一個數據進入網絡前要確定的東西。

  • udp&&tcp初識?

? ? ? ? 先來簡單初識一下TCP和UDP,在后面講解傳輸層時會詳解。TCP(Transmission Control Protocol 傳輸控制協議)和UDP(User Datagram Protocol 用戶數據報協議)都是傳輸層協議,但TCP需要連接、可靠傳輸、面向字節流,而UDP無需連接、不可靠傳輸、面向數據報,如下圖很具象地展現了TCP和UDP傳輸數據地過程。

? ? ? ? 其中,(無需)連接說的是udp沒有tcp在通信的三次握手與四次揮手,而是沒有建立連接過程,即“發送即結束”;可靠傳輸說的是tcp傳輸數據是不會出錯的,udp傳輸數據可以出錯;面向字節流與面向數據報是指看待數據的方式不同,tcp需要根據應用層協議對字節流作序列化和反序列化識別出一個報文,而udp直接默認拿到的就是一個報文。但是,綜上來看真的意思是tcp比udp好嗎?其實不然,兩者的使用需要看場景,tcp可以多應用在需要數據準確交付的場景,比如重要文件傳輸,而udp可以多應用在對傳輸錯誤也可容忍的場景,比如游戲,直播等實時性要求高的環境下(有時看直播會卡,但是無傷大雅)。

  • socket編程?

????????Socket編程是一種網絡編程技術,它提供了一種在不同計算機之間進行通信的方式。

基于TCP/IP協議,它允許計算機之間通過網絡進行數據傳輸。Socket編程提供了一些函數和方法,用于建立連接、傳輸數據和關閉連接。

????????在Socket編程中,有兩個主要的角色:服務器和客戶端。服務器程序監聽指定的端口,等待客戶端的連接請求。客戶端程序通過指定服務器的IP地址和端口,發起連接請求。以上會在下面詳細介紹。

網絡字節序

? ? ? ? 我們知道,內存中的多字節數據相對于內存地址有大端和小端之分,而網絡數據流也同樣有大端小端之分,因此,TCP/IP協議規定,網絡數據流應采用大端字節序,即低地址高字節.。不管這臺主機是大端機還是小端機,都要按照這個TCP/IP規定的網絡字節序來發送/接收數據,如果當前發送主機是小端,就需要先將數據轉成大端,否則就忽略,直接發送即可。那么,如下接口就是轉大端的相關接口,調用其可以做網絡字節序和主機字節序的轉換。

? ? ? ? 其中h表示host,n表示net,l表示32位長整數,s表示16位短整數,如果主機是小端字節序,這些函數將參數做相應的大小端轉換然后返回,如果主機是大端字節序,這些函數將不做轉換,將參數原封不動的返回。

socket常見接口

  • socket

功能:創建socket文件描述符(tcp和udp均會用到,服務器和客戶端均需要

參數

? ? ? ? domain:對于ipv4,指定為AF_INET,對于ipv6,指定為AF_INET6

? ? ? ? type:對于UDP,指定為SOCK_DGRAM,表示面向數據報的傳輸協議,對于TCP,指定為SOCK_STREAM,表示面向字節流的傳輸協議

? ? ? ? protocol:指定為0即可

返回值:成功返回一個文件描述符,類似讀寫文件一樣,向此文件描述符讀寫就是在網絡上收發數據,否則返回-1

  • bind

功能:綁定端口號(tcp和udp均會用到,僅服務器需要,因為服務器程序所監聽的網絡地址和端口號通常是固定不變的,客戶端程序得知服務器程序的地址和端口號后就可以向服務器發起連接,但是客戶端程序的端口號通常是變化的,所以不需要bind),將參數sockfd和myaddr綁定在一起,使sockfd這個用于網絡通訊的文件描述符監聽addr所描述的地址和端口號

參數

? ? ? ? sockfd:需要綁定的sock套接字,其實就是socket函數的返回值

? ? ? ? addr:通用指針類型,可以接受多種協議的sockaddr結構體地址(多種sockaddr結構體類型在下面介紹),但長短不同,需要傳入長度

? ? ? ? addrlen:addr指向的結構體長度

返回值:成功返回0,否則返回-1

sockaddr結構:?

?????????socket的接口適用于各種底層網絡協議,但是各種網絡協議的地址格式并不相同,又不想設計過多的接口,因此將所有接口進行統一,接口只有一套,協議格式統一使用struct sockaddr*,傳其他協議地址(sockaddr_in、sockaddr_un,如下圖)需要通過強轉,但若需要知道是哪一種套接字地址,可以通過16位地址類型判斷。

? ? ? ? 這三者中,本章節常用到sockaddr_in結構,也就是說常用到將sockaddr結構體強轉成sockaddr_in結構體,這里詳細說明一下sockaddr_in結構體,以下為其內部結構,在這個結構體中,最主要的就是端口號和IP地址,即圖中標星號的部分。

? ? ? ? 在我們寫的程序中一般是這樣初始化sockaddr結構體的(如下代碼塊),首先清零,再指定地址類型,然后填入ip和端口號信息。注意轉大端字節序,并且其中INADDR_ANY為宏(實則為0.0.0.0),表示本地的任意IP地址,則最后一個語句代表若需要指定ip地址則賦值給_ip,否則使用任意IP地址。

代碼:

struct sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(_port);
peer.sin_addr.s_addr = _ip.size() == 0 ? INADDR_ANY : inet_addr(_ip.c_str());
  • listen

功能:開始監聽socket(僅tcp會用到,僅服務器需要

參數

? ? ? ? sockfd:如上

? ? ? ? backlog:表示最多允許有backlog個客戶端處于連接等待狀態,如果接收到更多的連接請求就忽略,常設置為20,后面會詳細講到

返回值:成功返回0,否則返回-1

  • accept

功能:接受請求(僅tcp會用到,僅服務器需要),三次握手完成后,服務器調用accept()接受連接,如果服務器調用accept()時還沒有客戶端的連接請求,就阻塞等待直到有客戶端連接上來

參數

? ? ? ? sockfd:如上

????????addr和addrlen:輸出型參數,需提前定義傳入函數,函數返回后帶出來對方的ip和端口號信息,若傳NULL,則代表不關心客戶端的地址

返回值:成功返回一個文件描述符,否則返回-1

  • connect

功能:建立連接(僅tcp會用到,僅客戶端需要,因為客戶端需要調用此函數連接服務器)

參數

? ? ? ? sockfd:自己(客戶端)的fd

? ? ? ? addr和addrlen:傳入對方的IP地址和端口號信息

返回值:成功返回0,失敗返回-1

地址轉換函數

? ? ? ? 在上面介紹到的sockaddr中,sockaddr_in的成員sin_addr(struct in_addr)表示32位的IP地址,但是我們通常都是用點分十進制(比如192.168.1.10)表示IP地址,因此需要函數可以將字符串表示和in_addr之間來回轉換。

  • 字符串轉in_addr

? ? ? ? 首先,三者的功能是一樣的——將字符串轉in_addr,先看前兩個,cp就是需要轉換的字符串,inp是一個輸出型參數,結果由此參數傳出,而第二個函數是直接返回出來,第三個函數較于前兩個函數是新的,其中af可以填AF_INET或AF_INET6表示ipv4或ipv6,src還是傳入需要轉換的字符串,dst與inp的功能是一樣的,只是需要接收ipv4的in_addr或ipv6的in6_addr,因此設置為void*可以同樣接收(強轉)。

  • in_addr轉字符串

? ? ? ? 同樣地,in傳入in_addr,函數返回轉換地字符串形式,而inet_ntop是新接口,可以接收ipv6,src就是傳入不同類型的in_addr,dst接收轉換的字符串指針,size傳入字符串指針可以接收的字符串大小。

套接字讀寫函數

  • recvfrom&&recv

? ? ? ? 注意,recv用于tcp,recvfrom函數用于udp。可以看到,recvfrom函數除了比recv函數多了兩個參數,其他的參數是一樣的,因此只要介紹recvfrom即可。

sockfd:讀寫的套接字(文件描述符)

buffer&&len:讀取的數據所放的緩沖區及大小

flags:控制接收行為,通常可設為0

src_addr&&addrlen:輸出型參數,用來接收數據來源的地址信息及大小,注意addrlen在傳入時就要是sre_addr的大小

返回值:正常返回接收成功的字符個數若數據讀完或套接字關閉,則返回值為0,若出錯則返回-1并設置錯誤碼

  • sendto&&send

????????注意,send用于tcp,sendto函數用于udp。同樣地,sendto函數也是比send函數多了兩個參數。

sockfd:讀寫的套接字(文件描述符)

buf&&len:指向需要發送的數據的字符串指針及大小

flags:同recvfrom函數

dest_addr&&addrlen:不是輸出型函數,需要傳入需要發送的目的地址信息及大小

返回值:同recvfrom函數

注意:

????????思考一下recvfrom函數和sendto函數的使用順序,比如說,你使用sendto函數給張三發信息,此時你需要知道張三的地址信息,張三使用recvfrom函數接收到了你的信息,并且他通過輸出型參數得到了你的地址信息,之后張三拿著你的地址信息使用sendto函數給你回信,你通過recvfrom函數拿到了張三的回信并且拿到了他的地址信息。

? ? ? ? 在這個過程中,你與張三通信的前提是你知道張三的地址信息,你其實是知道的,無論是之前見面留下的聯系方式還是曾經通信過,你們之間必須至少有一方是知道的,否則無法通信。類比到服務器與客戶端,“你”就是客戶端,“張三”就是服務器,一般來說都是客戶端主動給服務器發信息,客戶端都是提前知道服務器的地址信息的,客戶端給服務器發送請求,服務器處理需求,將結果回信給客戶端,客戶端拿到需求即為享受到服務。

簡單udp服務器客戶端程序

  • udp服務器

? ? ? ? 下面的代碼塊是udp服務器的封裝及主程序調用。先看udp服務器封裝的類,屬性包括監聽的文件描述符sockfd、IP地址、端口,這些是一個udp服務器最基本需要的屬性,構造函數中傳入所決定的端口號、IP地址、sock(還沒有生成,初始化為-1)。

? ? ? ? 在啟動udp服務器前,先做一下準備工作——initserver(),創建監聽的文件描述符sock和綁定地址信息(包括端口號和IP地址),之后啟動udp服務器——start(),顯然是死循環運行,作為服務器,我們需要先使用recvfrom函數接收客戶端的請求(數據),返回值大于0,說明正確讀到了數據,這里當作字符串打印出來,并調用sendto函數將讀到的字符串寫回給客戶端(處理方式有很多,也可以客戶端傳來兩個數字,服務器計算出結果將其返回,但是這涉及到http協議的設定及序列化、反序列化)。最后若關閉服務器,莫要忘記close監聽的sockfd。

? ? ? ? 可以看到,udp服務器只能用到前面所談到的socket常用接口其中的兩個——socket()、bind(),這可以聯想到udp的特性——無連接,而listen()、accept()、connect()意味著是有連接,在tcp服務器的實現中才會用到。

代碼:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
using std::cout;
using std::endl;
#define SIZE 1024class UdpServer
{
public:UdpServer(uint16_t port,std::string ipAddr=""):_port(port),_ipAddr(ipAddr),_sock(-1){}bool initserver(){//1.創建套接字(ip+port)_sock=socket(AF_INET,SOCK_DGRAM,0);if(_sock<0){logMessage(FATAL,"%d:%s",errno,strerror(errno));exit(2);        }logMessage(NORMAL,"create socket success");//2.通過bind函數將此ip和端口號與此進程綁定struct sockaddr_in local;bzero(&local,sizeof(local)); //設置內容之前將所有字段設置為0local.sin_family=AF_INET;//端口需要發送給對方主機,需要通過網絡,因此將數據的字節序改為大端存儲local.sin_port=htons(_port);//ip也是一樣,但同時還需要將ip的字符串形式改為4個字節存儲,因此4個字節足夠存儲一個ip,//比如說192.168.10.1,每段數字都是[0,255],用1字節即可存儲//因此使用inet_addr函數,可同時完成上面兩件事//同時,INADDR_ANY表示服務器在工作過程中,可以從任意IP中獲取數據,也就是說不建議bind一個確定的地址local.sin_addr.s_addr = _ipAddr.size()==0 ? INADDR_ANY : inet_addr(_ipAddr.c_str());if(bind(_sock,(struct sockaddr*)&local,sizeof(local))<0){logMessage(FATAL,"%d:%s",errno,strerror(errno));exit(3);}logMessage(NORMAL,"bind success");return true;}void start(){char buffer[SIZE];for( ; ; ){//讀取數據struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t rf=recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(rf>0){buffer[rf]=0; //緩沖區末尾放上‘\0’,當作字符串uint16_t cli_port=ntohs(peer.sin_port);std::string cli_ip=inet_ntoa(peer.sin_addr);printf("[%s:%d]: %s\n",cli_ip.c_str(),cli_port,buffer);}//分析處理數據//寫回數據sendto(_sock,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,len);}}~UdpServer(){if(_sock>=0) close(_sock);}
private:uint16_t _port;std::string _ipAddr;int _sock;
};static void usage(std::string proc)
{cout<<"\nUsage:"<<proc<<" port\n"<<endl;
}int main(int argc,char* argv[])
{if(argc!=2){usage(argv[0]);exit(1);}//std::string ip=argv[1];uint16_t port=stoi(argv[1]);std::unique_ptr<UdpServer> server(new UdpServer(port));server->initserver();server->start();return 0;
}

  • udp客戶端

? ? ? ? 下面代碼塊是udp客戶端的實現(主程序實現,未封裝udp客戶端),與服務器實現并沒有本質的不同(但可以發現客戶端不需要bind)。通過命令行參數傳入服務器的地址信息(端口號和ip地址),首先依舊是創建監聽的文件描述符sock,之后就可以將服務器的地址信息填進struct sockaddr_in中,等待后面的sendto函數的使用,但是會發現客戶端無需bind,為什么?因為客戶端通過哪個端口與服務器通信并不重要,socket函數的函數體中會自動為客戶端程序選擇一個未被占用的端口號,并不需要用戶去操心,那又為什么服務器程序需要指定端口號呢?因為服務器的端口號和IP地址是需要固定的,不然客戶端程序如何得知新的變化的端口號和IP地址,因此需要調用bind函數固定下來。

? ? ? ? 顯然,客戶端程序主體也是一個死循環,先產生用戶的需求,之后調用sendto函數將需求發給服務器程序,服務器處理之后,客戶端調用rcvfrom函數接收結果,這里將結果顯示出來(舉例而已,也可以將結果作為下一次傳入的需求),若客戶端程序關閉,也莫要忘記關閉監聽的文件描述符。

代碼:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <unistd.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using std::cout;
using std::endl;
#define SIZE 1024static void usage(std::string proc)
{cout<<"\nUsage:"<<proc<<" ip port\n"<<endl;
}int main(int argc,char* argv[]) //傳入服務器ip、port
{if(argc!=3){usage(argv[0]);exit(1);}int sock=socket(AF_INET,SOCK_DGRAM,0);if(sock<0){std::cerr<<"socket error"<<endl;exit(2);}uint16_t serverport=atoi(argv[2]);std::string serverip=argv[1];std::string message; //從鍵盤拿信息char buffer[SIZE]; //緩沖區struct sockaddr_in server;memset(&server,0,sizeof(server)); //提前置零server.sin_addr.s_addr=inet_addr(serverip.c_str());server.sin_port=htons(serverport);while(true){cout<<"請輸入:";std::getline(std::cin,message);if(message=="quit")break;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));struct sockaddr_in tmp;socklen_t len=sizeof(tmp);ssize_t rf=recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&tmp,&len); //tmp與server相同,都是服務端ip、port相關信息if(rf>0){buffer[rf]=0;cout<<"server # "<<buffer<<endl;}}close(sock);return 0;
}

?運行結果:

簡單tcp服務器客戶端程序? ? ? ??

  • tcp服務器?

? ? ? ? 以下代碼塊是tcp服務器的封裝及主程序調用,先看tcp服務器封裝,屬性和構造函數與udp服務器封裝一樣。對于initserver(),可以看到,調用socket創建一個文件描述符及bind()綁定地址信息也都是一樣的,但是tcp服務器緊接著調用listen()監聽sockfd是否有客戶端連接,這是tcp服務器必須要做的,其實本質就是使sockfd成為一個監聽描述符。

? ? ? ? 之后,依舊是死循環開啟tcp服務器——start()。首先,服務器會調用accept函數去阻塞等待客戶端的連接,當有客戶端調用connect函數去建立連接時,accept函數就會返回一個新的文件描述符serverfd,這個文件描述符專門用來和客戶端通信,而原本的監聽描述符繼續監聽客戶端的connect函數申請連接,若存在就會繼續返回另一個新的文件描述符與此客戶端通信。這個過程就好像是,監聽文件描述符是一個在飯店門口拉客的服務員,當拉到客人之后進店就會找另一個服務員(與此客戶端通信的文件描述符)來接待這個客人(比如點菜,服務),而拉客服務員繼續去門外拉客。

? ? ? ? 當accept函數返回一個新的文件描述符來與客戶端通信,服務器就可以通過此文件描述符收到客戶端的需求,并返回應答給客戶端,這里封裝成了一個服務函數——service(),首先recv函數(也可以read函數)讀取客戶端的數據,處理以后通過write函數(也可以send函數)返回結果給客戶端,這里依舊是把數據當作字符串并寫回給客戶端。

代碼:

#include <iostream>
#include <string>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"using std::cout;
using std::endl;
using std::string;static void service(int servicesock,string cli_ip,uint16_t cli_port)
{char buffer[1024];while(true){int n=read(servicesock,buffer,sizeof(buffer)-1);if(n>0){buffer[n]='\0';cout<<cli_ip<<"-"<<cli_port<<"# "<<buffer<<endl;}else if(n==0) //連接被關閉了{logMessage(NORMAL,"link shutdown");break;}else{logMessage(ERROR,"read error");break;}string msg;msg+=buffer;write(servicesock,msg.c_str(),msg.size());}
}class TcpServer
{
private:const static int g_backlog=20;
public:TcpServer(uint16_t port,string ip=""):_port(port),_ip(ip),_sock(-1){}void initserver(){//創建socket_sock=socket(AF_INET,SOCK_STREAM,0);if(_sock<0){logMessage(FATAL,"create socket error");exit(2);}//bindstruct sockaddr_in peer;memset(&peer,0,sizeof(peer));peer.sin_port=htons(_port);peer.sin_family=AF_INET;peer.sin_addr.s_addr=_ip.size()==0 ? INADDR_ANY : inet_addr(_ip.c_str());if(bind(_sock,(struct sockaddr*)&peer,sizeof(peer))<0){logMessage(FATAL,"bind error");exit(3);}//listen函數用以建立連接,相當于飯店的一個服務員在外面拉客,其中_sock就是在外面拉客的服務員if(listen(_sock,g_backlog)<0){logMessage(FATAL,"listen error");exit(4);}logMessage(NORMAL,"initserver success");}void start(){while(true){struct sockaddr_in peer;socklen_t len=sizeof(peer);int servicesock=accept(_sock,(struct sockaddr*)&peer,&len); //_sock將拉的客人帶到飯店,店里接待的服務員出來一個招呼他,其中servicesock就是一個招待的服務員if(servicesock<0){logMessage(ERROR,"accept error");continue;}logMessage(NORMAL,"accept success");uint16_t clientport=ntohs(peer.sin_port);string clientip=inet_ntoa(peer.sin_addr);service(servicesock,clientip,clientport);}}~TcpServer(){if(_sock>0)close(_sock);}
private:uint16_t _port;string _ip;int _sock;
};static void usage(string proc)
{cout<<"usage: "<<proc<<" port"<<endl;
}//    ./tcpserver port
int main(int argc,char* argv[])
{if(argc!=2){usage(argv[0]);exit(1);}uint16_t serverport=atoi(argv[1]);unique_ptr<TcpServer> server(new TcpServer(serverport));server->initserver();server->start();return 0;
}

  • tcp客戶端

? ? ? ? tcp客戶端也并未做出封裝,依舊是在主程序中直接實現,與udp客戶端程序實現的差別在于socket函數創建文件描述符(依舊不需要bind函數)之后,調用connect函數與服務器建立連接,成功以后就可以正常的通信了,這一點與udp客戶端實現也是一樣。

? ? ? ? 這里重點講的就是,其實connect函數就是在發起三次握手,三次握手成功以后服務器的accept函數就會返回一個與客戶端通信的文件描述符。也可以提一嘴,四次揮手是在客戶端關閉(close函數)了socket函數創建出的文件描述符,詳細會在傳輸層詳解中講到。

代碼:

#include <iostream>
#include <string>
#include <string.h>
#include <cstdlib>
#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "thread.hpp"
#include "log.hpp"using std::cout;
using std::cerr;
using std::endl;
using std::string;static void usage(string proc)
{cout<<"usage: "<<proc<<" port"<<endl;
}//   ./tcpclient serverip serverport
int main(int argc,char* argv[])
{if(argc!=3){usage(argv[0]);exit(1);}string serverip=argv[1];uint16_t serverport=atoi(argv[2]);int sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){cerr<<"create socket error"<<endl;exit(2);}struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(serverport);server.sin_addr.s_addr=inet_addr(serverip.c_str());if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0){cerr<<"connect error"<<endl;exit(3);}cout<<"connect success"<<endl;while(true){string msg;cout<<"請輸入:";getline(cin,msg);if(msg=="quit")break;send(sock,msg.c_str(),msg.size(),0);char buffer[1024];ssize_t s=recv(sock,buffer,sizeof(buffer)-1,0);if(s>0){buffer[s]=0;cout<<"server# "<<buffer<<endl;}else if(s==0){cout<<"link break"<<endl;break;}elsebreak;}close(sock);return 0;
}

?運行結果:

后記

? ? ? ? 在本章節中,我們先從網絡編程所需要的概念基礎下手,再從運用的視角去詳細介紹了各種供數據在網絡中傳輸的socket接口,接著編寫簡單的udp、tcp服務器綜合使用這些接口加以鞏固。雖接口看起來很多并且細節也多而且復雜,但是用于網絡數據傳輸的接口大部分也就涉及到這些,也就是說只要掌握這些,網絡傳輸的功能就掌握了一大半了,因此在學習這些接口時要仔細,最后的簡單udp、tcp服務器可以反復的獨立編寫,加強記憶,在學習后面較為復雜的服務器時可以相對輕松,加油。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11395.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11395.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11395.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java入門基礎學習筆記2——JDK的選擇下載安裝

搭建Java的開發環境&#xff1a; Java的產品叫JDK&#xff08;Java Development Kit&#xff1a; Java開發者工具包&#xff09;&#xff0c;必須安裝JDK才能使用Java。 JDK的發展史&#xff1a; LTS&#xff1a;Long-term Support&#xff1a;長期支持版。指的Java會對這些版…

pycharm報錯Process finished with exit code -1073740791 (0xC0000409)

pycharm報錯Process finished with exit code -1073740791 (0xC0000409) 各種垃圾文章&#xff08;包括chatgpt產生的垃圾文章&#xff09;&#xff0c;沒有給出具體的解決辦法。 解決辦法就是把具體報錯信息顯示出來&#xff0c;然后再去查。 勾選 然后再運行就能把錯誤顯示…

MetaRTC-play拉流客戶端代碼分析

渲染使用opengl&#xff0c;音頻播放使用alsa。 當點擊播放按鈕后&#xff0c;以此調用的類如下&#xff0c;開始建立rtc連接&#xff0c;AV解碼&#xff0c;音頻渲染&#xff0c;視頻渲染。 如果想去除QT&#xff0c;改為cmake工程管理&#xff0c;去掉渲染部分即可。 下方是…

VUE+PrintJS打印-邊距設置問題(提供解決方案)

VUE打印我們一般用printJS&#xff0c;雖然它也提供了邊距設置&#xff0c;但不管怎么調&#xff0c;感覺都不對&#xff0c;也換其他組件試過&#xff0c;沒啥區別&#xff0c;并不能解決問題。 今天又發來個需求&#xff0c;要求設置打印頁面的上、下、左、右邊距&#xff0…

Linux---vim編輯器(續寫)

5. vim正常模式命令集 插入模式 按「i」切換進入插入模式「insert mode」&#xff0c; 按“i”進入插入模式后是從光標當前位置開始輸入文件&#xff1b; 按「a」進入插入模式后&#xff0c;是從目前光標所在位置的下一個位置開始輸入文字&#xff1b; 按「o」進入插入模式…

從頭開始學Spring—01Spring介紹和IOC容器思想

目錄 1.Spring介紹 1.1Spring概述 1.2特性 1.3五大功能模塊 2.IOC容器 2.1IOC思想 ①獲取資源的傳統方式 ②反轉控制方式獲取資源 ③DI 2.2IOC容器在Spring中的實現 ①BeanFactory ②ApplicationContext ③ApplicationContext的主要實現類 1.Spring介紹 1.1Sprin…

Linux系統一步一腳印式學習

Linux操作系統具有許多特點和優勢。首先&#xff0c;它是開放源代碼的&#xff0c;也就意味著任何人都可以對源代碼進行查看和修改。其次&#xff0c;可以同時支持多個用戶且可以同時執行多個任務&#xff0c;此外&#xff0c;Linux操作系統也非常穩定和安全。相對于其他操作系…

銀川崗位外包有邦芒 讓崗位空缺不再成為難題

銀川邦芒人力崗位外包服務&#xff0c;專為企業量身定制&#xff0c;涵蓋從崗位人員招聘到勞動關系終止的全方位、無風險外包解決方案。借助我們的崗位外包&#xff0c;企業可以在確保用工穩定的同時&#xff0c;將更多資源聚焦于核心業務的發展與創新。 我們提供全面、高效的外…

安全測試|常見SQL注入攻擊方式、影響及預防

SQL注入 什么是SQL注入&#xff1f; SQL注入是比較常見的網絡攻擊方式之一&#xff0c;主要攻擊對象是數據庫&#xff0c;針對程序員編寫時的疏忽&#xff0c;通過SQL語句&#xff0c;實現無賬號登錄&#xff0c;篡改數據庫。 SQL注入簡單來說就是通過在表單中填寫包含SQL關鍵…

SSD-60S施耐德電機保護器EOCR-SSD

EOCR主要產品有電子式電動機保護繼電器&#xff0c;電子式過電流繼電器&#xff0c;電子式欠電流繼電器&#xff0c;電子式欠電壓繼電器&#xff0c;其它保護裝置&#xff0c;電流互感器。EOCR-SSD 10-60A電機保護器 系列型號: EOCRSSD-05SEOCRssD-30s EOCRSSD-60SEOCRSSD-0…

Linux文件管理(超詳細講解)

Linux文件管理 一、管理文件1.目錄和路徑2.目錄操作命令3.文件操作命令4.文件內容操作命令 一、管理文件 1.目錄和路徑 文件與目錄簡介 文件是Linux操作系統用來存儲信息的基本結構&#xff0c;是一組信息的集合。目錄是一種特殊的文件&#xff0c;用來保存文件及其相關信息。…

開源即時通訊IM框架 MobileIMSDK v6.5 發布

一、更新內容簡介 本次更新為次要版本更新&#xff0c;進行了bug修復和優化升級&#xff08;更新歷史詳見&#xff1a;碼云 Release Notes、Github Release Notes&#xff09;。 MobileIMSDK 可能是市面上唯一同時支持 UDPTCPWebSocket 三種協議的同類開源IM框架。輕量級、高…

8種常見的CMD命令

1.怎么打開CMD窗口 步驟1&#xff1a;winr 步驟2&#xff1a;在彈出的窗口輸入cmd&#xff0c;然后點擊確認&#xff0c;就會出現一個cmd的窗口 2.CMD的8種常見命令 2.1盤符名稱冒號 說明&#xff1a;切換盤的路徑 打開CMD窗口這里默認的是C盤的Users的27823路徑底下&#xf…

基于微信小程序+JAVA Springboot 實現的【網上商城小程序】app+后臺管理系統 (內附設計LW + PPT+ 源碼+ 演示視頻 下載)

項目名稱 項目名稱&#xff1a; 基于微信小程序的網上商城 項目技術棧 該項目采用了以下核心技術棧&#xff1a; 后端框架/庫&#xff1a; Java, SSM框架數據庫&#xff1a; MySQL前端技術&#xff1a; 微信開發者工具&#xff0c;微信小程序框架 項目展示 5.1 管理員服務…

Mat: Unknown HPROF Version

問題&#xff1a;Mat 加載 android studio 導出的 hprof 文件失敗 原因&#xff1a;android hprof 文件不是標準的 java hprof 文件 解決辦法&#xff1a; 使用 android sdk 自帶的命令將 hprof 轉換成標準的 java hprof

瞬息全宇宙——穿越之旅終極教程,手把手教你做出百萬點贊視頻

最近一種叫“瞬息全宇宙”的視頻火了&#xff0c;抖音一期視頻百萬贊&#xff0c;各個博主視頻都在帶瞬息全宇宙這個標簽&#xff0c;于是就有很多朋友催我出教程了&#xff0c;在琢磨了幾天之后&#xff0c;終于整出來了 教程包含了插件的安裝&#xff0c;界面的講解&#xff…

生產制造行業推拉式生產的復合應用

一、案例分析&#xff08;汽配行業&#xff09; 重點&#xff1a; 1. MTO/MTS 與 PUSH/PULL 有關系但是不是充分關系 2. MTO/MTS 是公司經營策略&#xff0c;更多是對市場需求的經營策略&#xff0c;體現在生產時機上的不同&#xff0c;一個是等客戶需求&#xff0c;一個是填…

HTML4(三):表單

文章目錄 表單1. 基本結構2. 常用表單控件2.1 文本輸入框2.2 密碼輸入框2.3 單選框2.4 復選框2.5 隱藏域2.6 提交按鈕2.7 重置按鈕2.8 普通按鈕2.9 文本域2.10 下拉框2.11 示例 3. 禁用表單控件4. lable標簽5. fieldset與legend標簽6. 總結 表單 概念&#xff1a;一種包含交互…

Raft論文閱讀筆記+翻譯:In Search of Understandable Consensus Algorithm

In Search of Understandable Consensus Algorithm 摘要 Raft是一種管理復制日志的共識算法。它產生與&#xff08;多&#xff09;Paxos等效的結果&#xff0c;并且與Paxos一樣高效&#xff0c;但其結構與Paxos不同。這使得Raft比Paxos更易理解&#xff0c;也為構建實際系統提供…

近嶼OJAC帶你解讀:什么是大模型幻覺?

忠實性幻覺也可以細分&#xff0c;分為指令不一致&#xff08;輸出偏離用戶指令&#xff09;、上下文不一致&#xff08;輸出與上下文信息不符&#xff09;、邏輯不一致三類&#xff08;推理步驟以及與最終答案之間的不一致&#xff09;。 具體解析 大模型產生幻覺的原因可能…