日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

如何连接Linux上的服务器 网络编程,Linux 网络编程 一

發布時間:2025/3/11 linux 11 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何连接Linux上的服务器 网络编程,Linux 网络编程 一 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、網絡編程基礎

網絡編程本身是一門很大的學問,涉及到的東西也很多,尤其是各種協議。先看圖:

正如上圖所示,網絡編程中包含五大層面(也有區分六個層面),從應用層到物理層可以明顯看出 越往下越接近計算機硬件。自己并不是專業網絡編程的工程師,所以僅對這五大層面有一點點粗淺的了解,這篇文章網絡編程技巧博主寫的比較詳細. 平時大多數所謂網絡編程,其實是在傳輸層、網絡層方面.

二、socket編程

首先,socket(套接字)編程應該屬于傳輸層,主要實現的是端到端的通信,非常類似于很久很久以前的固話通信,應用程序可以通過它發送或者接受數據,可以對它進行像文件似得讀寫、關閉等操作。套接字允許應用程序將I/O插入網絡中,并與網絡中的其他應用程序進行通信。

其次,網絡中兩臺或多臺主機之間進行通信,必須知道對應主機的地址,也就是其IP地址,但是只知道IP地址是遠遠不夠的,試想如你在本機A發送了一個消息,另一個臺主機B也接收到到了該消息,但是到底是B主機的哪一個進程接受并處理該消息?就像你用QQ給B發送消息,但是B不可能通過陌陌收到該消息。 因此,相互通信的主機之間還必須確定一一對應的消息處理接口--端口。端口的存在,主要是為了確認消息一一對應性。另外,端口號其實就是一個從0開始的到65535之間的一個整型數字,0~1023端口,也就是常說的靜態端口,已被操作系統另做它用(http,https,ftp等各種協議占用),我們自己所能使用的端口范圍只能從1024開始,即動態端口取值[1024,65535].

可是看出,若要進行網絡間通信,socket至少要包含IP+port兩個方面,其實事實也是如此.還是以有線電話做為類比,socket其實就是自己家中的一部電話,其中IP就是家庭地址,port就是自己家的電話號碼,當要給別人打電話時,別人家當然也必須有自己的座機和專屬于該座機的號碼.

或許我們也能猜出,socket編程是網絡編程里邊必不可少且及其重要的一個環節.

三、Linux+socket實踐

1、目的

熟悉Linux(這里用Ubuntu16.04版本,其他版本類似)下socket編程基本流程,掌握socket編程基本原理,搞懂Linux下socket編程所必須的函數及其用法.

實驗:在本地模擬兩臺機器,服務器和客戶端,服務器監聽客戶端信息并能發送廣播,客戶端可以主動給服務器發送消息,其中消息的輸入是從標準輸入設備輸入,并輸出到標準輸出--Linux 終端.

開始之前必須了解一點 什么是文件描述符,在Unix Linux系統中,文件描述符是一個非負整數,其存在作用更像一個索引,系統內核通過該"索引"找到對應的文件、設備、外設、安裝的軟件等等, 并通過描述符對它們進行操作。總而言之,文件描述符對應了系統上的所有文件,這里的文件并非"傳統意義上的普通文件",而是指Linux系統內核所能管理1的一切,包含文檔、文件、硬件設備、系統軟件等等。這也體現了Linux系統的設計思想----把一切視作文件.

2、必要接口

1)、socket函數

既然socket這么重要,來看它到底是個什么東西.在Linux終端執行:man socket,出現:

通過Linux手冊查詢可以知道該函數所必須的頭文件,函數聲明和函數描述等信息.從[DESCRIPTION]字段可知,函數創建了一個用于通信的端點并返回該端點的描述符,若創建成功,返回創建套接字的文件描述符,否則返回一負數.

函數聲明 int socket(int domain,int type,int protocol);

參數 domain:表示創建該socket所使用的通訊協議家族--地址族,現在一般用IPv4協議,所以通常會選擇AF_INET;

參數type:指定所需的通信類型。包括數據流(SOCK_STREAM)TCP協議、數據報(SOCK-DGRAM)UDP協議和原始類型(S0CK_RAW)新網格協議的開發測試.

參數protocol:說明該套接字使用的協議族中的特定協議。如果不希望特別指定使用的協議,則置為0,使用默認的連接模式.

若要進行 基于TCP IP的網絡開發測試,則函數創建方式一般為:

int listenfd = socket(AF_INET,SOCK_STREAM,0);

2)、bind函數

既然有了一部“電話”,那么就需要為該電話綁定唯一的“所屬地址”,同樣Linux命令行執行:man bind,同樣函數聲明為:

int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

從手冊的描述中可以看出,當成功創建socket套接字后,調用該函數可以將所創建的套接字(sockfd)和指定的地址(addr)綁定.

地址是由這樣一個結構體指定:

struct sockaddr {

sa_family_t sa_family; //地址族

char sa_data[14]; //14字節的協議地址

}

上面struct sockaddr是通用地址,在網絡編程中 internet sockaddr使用下面地址,兩種地址可以互換:

struct sockaddr_in {

short int sin_family; /* 地址族,AF_xxx 在socket編程中只能是AF_INET */

unsigned short int sin_port; /* 端口號 (使用網絡字節順序) */

struct in_addr sin_addr; /* 存儲IP地址 4字節 */

unsigned char sin_zero[8]; /* 總共8個字節,實際上沒有什么用,只是為了和struct sockaddr保持一樣的長度 */

};

bind()函數的第三個參數表示地址所占字節長度,socklen_t本質上是一個 unsigned int宏定義.

可以通過這樣方式指定地址:

struct sockaddr_in serveraddr;

memset(&serveraddr,0,sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(5188);

serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

//serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

首先聲明網絡接口地址結構,在給該地址賦值前必須將其清空.依次設置該地址的地址族、IP和端口(這里隨便設置了一個),上邊出現另一個新函數htons,同樣終端下man htons,可知該函數的主要作用是將主機字節序轉化為網絡字節序,關于這兩個字節序后續再深入研究.這里可以理解為:htons()的主要作用就是將十進制的ip地址和端口號轉化為網絡可以識別的"東東".

至此,基本可以完成座機的安裝入戶和號碼綁定:

bing(listenfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));

3)、listen監聽函數

對于我們的服務器而言,它需要監聽來自客戶端發來的消息,Linix終端中 man listen可以看到詳細信息. 函數聲明為:

int listen(int socdfd,int backlog);

其中參數sockfd代指所要監聽的套接字文件描述符,參數backlog表示在套接字掛起時,所能接受請求的最大隊列長度.函數執行成功返回 0,否則返回 -1.

必須說明一點,當調用該函數后,參數socdfd所指定的套接字將變為被動套接字,所謂被動套接字,是指其只能用來接收來自其他用戶的鏈接請求. 類似于改變了套接字的狀態,使其只能用于接收.

4)、accept 接收函數

對于我們的服務器而言,由于其只具備接收功能,因此必須創建一個接受函數:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

函數參數不言自明,參數1sockfd表示服務器socket描述符,參數2是指客戶端的協議地址,參數3為地址長度. 函數成功返回監聽的等待隊列中第一個套接字的描述符.

3、服務器實現

服務器的功能是監聽客戶端發來的消息,并將消息廣播給客戶端.因此需要一個循環實時監聽客戶端發來的消息,在本地構建一個簡單的服務器如下:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define ERR_EXIT(m) \

do \

{ \

perror(m);\

exit(EXIT_FAILURE);\

}while(0)

int main(void){

int listenfd;

if(( listenfd = socket(PF_INET,SOCK_STREAM,0)) < 0){

ERR_EXIT("socket");

}

struct sockaddr_in serveraddr;

memset(&serveraddr,0,sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(5188);

serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

//serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

if(bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)

ERR_EXIT("bind");

//一旦監聽,則為被動套接字(只能接受連接,調用accep函數之前調用),這里隨便給了一個最大隊列長度

if(listen(listenfd,100)< 0)

ERR_EXIT("listen");

//聲明一個地址,用于存儲客戶端鏈接時的協議地址

struct sockaddr_in peeraddr;

socklen_t peerlen = sizeof(peeraddr);

int conn; //返回的一個主動套接字

if((conn= accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)

ERR_EXIT("accept");

char recvbuff[1024];

while(1){

memset(recvbuff,0,sizeof(recvbuff));

int ret = read(conn,recvbuff,sizeof(recvbuff));

fputs(recvbuff,stdout);

write(conn,recvbuff,ret);

}

close(listenfd);

return 0;

}

其中用到了幾個非socket API的函數:

ssize_t read(int fd,void *buf,size_t count);

ssize_t write(int fd, const void *buf, size_t count);

read()函數:負責從fd所指定文件描述符讀取字節大小為count的數據到buf中.若成功返回實際讀取到的字節大小,否則返回負數,返回0表示讀取到文件結束.

write():將buf中的count個字節內容寫入文件描述符fd.成功時返回寫的字節數.

4、客戶端實現

客戶端的實現和服務器的實現之間大同小異,同樣都需要 ” 安裝電話 “ ,但是客戶端的功能僅在于向外”撥打電話“. 區別在于客戶端是主動發起連接請求,所以它必須知道自己所要連接的目標,之后服務器才有響應.同樣客戶端并不需要監聽,只需要接收到服務器的廣播即可. 發起連接請求需要函數 connect:

int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

在上述連接函數中,參數sockfd表示本機(客戶端)的socket套接字描述符,參數addr表示服務器端的地址,參數3表示地址長度.

代碼實現:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define ERR_EXIT(m) \

do \

{ \

perror(m);\

exit(EXIT_FAILURE);\

}while(0)

int main(void){

int sock;

if(( sock = socket(PF_INET,SOCK_STREAM,0)) < 0){

ERR_EXIT("socket");

}

struct sockaddr_in serveraddr;

memset(&serveraddr,0,sizeof(serveraddr));

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(5188);

// serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

//發起連接

connect(sock,(struct sockadddr*)&serveraddr,sizeof(serveraddr));

char recvbuf[1024]={0};

char sendbuf[1024]={0};

while(fgets(sendbuf,sizeof(sendbuf),stdin)!= NULL){

write(sock,sendbuf,strlen(sendbuf));

read(sock,recvbuf,sizeof(recvbuf));

fputs(recvbuf,stdout);

memset(recvbuf,0,sizeof(recvbuf));

}

close(sock);

return 0;

}

上述函數功能就是從客戶端主動向服務器發送連接請求,并在客戶端機器的標準設備上如字符,服務器接受并返回. 實現兩臺機器通信的模擬.

5、結果

效果如下圖:

用gcc編譯上述兩個文件,首先啟動服務器,之后啟動客戶端.在客戶端隨便輸入字符,服務器解收到并廣播返回. 至此基本完成目的.

三、總結

目前來看,創建服務器的一般流程是:

1.創建socket套接字(`socket`函數);

2.創建服務器地址,地址包含協議族、IP和端口號(`const struct sockaddr*`);

3.綁定套接字和服務器地址(bind函數);

4.系統監聽服務器,一旦監聽則該套接字變為被動套接字,只能用于接收數據(`listen`函數);

5.作為服務器,應該能接收客戶端信息(`accept`函數),該函數返回一個主動套接字;

基于以上步驟,基本能搭建一個簡單的服務器.

客戶端的搭建相比而言簡單許多:

1.創建用于連接的套接字;

2.將套接字和服務器地址連接;

3.發送消息

網絡編程畢竟浪大水深,畢竟初涉,慢慢填充.

總結

以上是生活随笔為你收集整理的如何连接Linux上的服务器 网络编程,Linux 网络编程 一的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。