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

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux网络编程(Socket)

發布時間:2023/12/10 linux 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux网络编程(Socket) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 網絡編程(Socket)概述
    • 引入
    • 網絡編程通識掃盲
    • socket套接字
    • 套接字描述符
    • 字節序
  • socket編程步驟
  • Linux提供的API簡析
    • 創建套接字即連接協議[socket](服、客)
    • 綁定IP和端口[bind](服)
      • 地址轉換api
      • 字節序轉換api
    • 監聽[listen](服)
    • 接受連接[accept](服)
    • 數據收發[read、write](服、客)
    • 客戶端的[connect]函數(客)
  • socket服務端代碼實現
  • socket客戶端代碼實現
  • 實現雙方(多方)一直聊天
  • 多方消息收發

網絡編程(Socket)概述

引入

前面幾個章節講的進程間通訊均基于同一臺Linux內核實現的,因此無法實現多機(和手機、單片機、X86架構等)通訊,因此引入網絡通訊,入門先學習Socket(又叫做套接字)網絡編程

問題:兩臺計算機實現TCP(通過socket編程)通信時,要用到線么??總感覺僅僅通過代碼就能建立連接不太靠譜。
回答:TCP連接的基礎就是網絡連接已經建立好之后,所以物理連接肯定是基礎。至于物理連接有很多種,可以是有線的、也可以是無線的,只要協議支持TCP/IP協議就可以。

網絡編程通識掃盲


socket套接字

socket起源于Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。Socket就是該模式的一個實現, socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)。

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。

套接字描述符

其實就是一個整數,我們最熟悉的句柄是0、1、2三個,0是標準輸入,1是標準輸出,2是標準錯誤輸出。0、1、2是整數表示的,對應的FILE *結構的表示就是stdin、stdout、stderr。當應用程序要創建一個套接字時,操作系統就返回一個小整數作為描述符,應用程序則使用這個描述符來引用該套接字需要I/O請求的應用程序請求操作系統打開一個文件。操作系統就創建一個文件描述符提供給應用程序訪問文件。從應用程序的角度看,文件描述符是一個整數,應用程序可以用它來讀寫文件。

字節序

字節序就是字節存儲的順序(從高地址開始存儲還是從低地址開始存儲),在網絡編程中要注意相關協議使用的字節序,防止數據傳輸出錯。具體使用的是字節序轉換api,配合端口號使用

socket編程步驟

模擬場景:

步驟介紹:

Linux提供的API簡析

創建套接字即連接協議[socket](服、客)

int socket(int domain,int type,int protocol)

綁定IP和端口[bind](服)

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

struct sockaddr 這個結構體一般同等替代成struct sockaddr_in,使用的時候注意新結構體的類型強制轉換。

地址轉換api

int inet_aton(const char *straddr, struct in_addr *addrp);char *inet_ntoa(struct in_addr inaddr);

字節序轉換api

監聽[listen](服)

int listen(int sockfd, int backlog);

backlog:支持最大的連接數

接受連接[accept](服)

三次握手成功就建立accept連接。函數里面的結構體存放客戶端的IP和端口號等信息!

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

數據收發[read、write](服、客)

和文件read、write用的同一個api。底下最后兩對一般用于UDP

數據收發第二套API,多了flags控制參數

客戶端的[connect]函數(客)

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

socket服務端代碼實現

利用telnet的方式進行通信,目前還沒有寫客戶端的代碼

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(int argc, char const *argv[]) {int s_fd;int c_fd;int n_read;int n_write;char readBuf[128];char *returnMsg="我收到了你的信息";//發送給客戶端的消息 盡量不使用數組struct sockaddr_in c_addr;struct sockaddr_in s_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));memset(&s_addr,0,sizeof(struct sockaddr_in));//數據清空 再配置//1.socket int socket(int domain, int type, int protocol);s_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協議if(s_fd == -1){printf("創建socket失敗");perror("socket:");exit(-1);}//2.bind int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);s_addr.sin_family=AF_INET;//ipv4s_addr.sin_port=htons(8687);//端口號,選擇5000以上(有些端口被系統調用)。honts返回網絡字節序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton("192.168.103.49",&s_addr.sin_addr)//inet_aton("127.0.0.1",&s_addr.sin_addr);//sin_addr是結構體sockaddr_in里面的結構體 存放IP(下面有查找到原型) 然后轉換為網絡能識別的格式//或者使用ifconfig命令查到實際本機的IP也可以bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//結構體類型轉換,因為用的同等替換的結構體sockaddr_in//3.listen int listen(int sockfd, int backlog);listen(s_fd,10);//監聽10個連接//4.accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int client=sizeof(struct sockaddr_in); //要求用指針(存放長度)c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&client);//sockaddr_in進行類型強轉 存放客戶端信息if(c_fd == -1){printf("連接失敗\n");perror("accept:");exit(-1);}//客戶端printf("客戶端的ip:%s\n",inet_ntoa(c_addr.sin_addr)); //把網絡格式的ip地址打印成字符串格式//5.read ssize_t read(int fd, void *buf, size_t count);n_read=read(c_fd,readBuf,128); //c_fd客戶端if(n_read == -1){perror("read:");}else{printf("得到的消息:%d,%s\n",n_read,readBuf);}//6.write ssize_t write(int fd, const void *buf, size_t count);n_write=write(c_fd,returnMsg,strlen(returnMsg));return 0; }

運行結果(客戶端發送 huai dan):

Tips:在user/include目錄下查找頭文件。查找結構體sockaddr_in的定義原型、使用了哪個頭文件時,我們可以用grep xx* -nir 來實現(n:顯示行號;i:不區分大小寫;r:遞歸)。

進去就能找到這個結構體原型啦:

socket客戶端代碼實現

客戶端向服務端發送消息,實現通信(只能進行一次通訊)。

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(int argc, char const *argv[]) {int c_fd;int n_read;int n_write;int c_connect;char readBuf[128];char *returnMsg="這是來自客戶端的信息";struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));//數據清空//1.socket int socket(int domain, int type, int protocol);c_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協議if(c_fd == -1){printf("創建socket失敗");perror("socket:");exit(-1);}c_addr.sin_family=AF_INET;//ipv4c_addr.sin_port=htons(8687);//端口號,選擇5000以上。honts返回網絡字節序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton("192.168.103.49",&c_addr.sin_addr);//轉換為網絡能識別的格式//2.connect int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);c_connect=connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr));if(c_connect == -1){printf("連接失敗\n");perror("connect:");}//3.write/send ssize_t write(int fd, const void *buf, size_t count);n_write=write(c_fd,returnMsg,strlen(returnMsg));//注意和sizeof的使用區別//4.read ssize_t read(int fd, void *buf, size_t count);n_read=read(c_fd,readBuf,128);if(n_read == -1){perror("read:");}else{printf("來自服務端的消息:%d,%s\n",n_read,readBuf);}//5.close int close(int fd);close(c_fd);return 0; }

運行結果:

實現雙方(多方)一直聊天

本質就是上面的代碼加入while循環,實現不斷的消息收發

其中服務端使用了兩次fork():

  • 第一次是在accept后(即三次握手成功后)創建進程,實現和多個客戶端的通信;
  • 第二次fork()創建進程應用在和客戶端通信“寫”的過程,而“讀”放在while循環中,這樣就實現了讀和寫并行運行。

客戶端僅用fork創建了一個進程,同樣應用在“寫”,“讀”放在while循環中,這樣就實現了讀和寫并行運行。

服務端代碼:

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(int argc, char const *argv[]) {int s_fd;int c_fd;int n_read;int n_write;char readBuf[128];char returnMsg[128]={0};struct sockaddr_in c_addr;struct sockaddr_in s_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));memset(&s_addr,0,sizeof(struct sockaddr_in));//數據清空if(argc != 3){printf("參數出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);s_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協議if(s_fd == -1){printf("創建socket失敗");perror("socket:");exit(-1);}//2.bind int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);s_addr.sin_family=AF_INET;//ipv4s_addr.sin_port=htons(atoi(argv[2]));//端口號,選擇5000以上。honts返回網絡字節序,atoi(argv[2])防止端口被占用//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&s_addr.sin_addr);//轉換為網絡能識別的格式bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen int listen(int sockfd, int backlog);listen(s_fd,10);//監聽10個連接//4.accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int client=sizeof(struct sockaddr_in);while(1){//不斷接收客戶端c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&client);if(c_fd == -1){printf("連接失敗\n");perror("accept:");exit(-1);}printf("客戶端的ip:%s\n",inet_ntoa(c_addr.sin_addr)); //把網絡格式的ip地址打印成字符串格式if(fork() == 0){if(fork() == 0){while(1){//不斷寫入memset(returnMsg,0,sizeof(returnMsg));printf("請輸入:\n");gets(returnMsg);//6.write ssize_t write(int fd, const void *buf, size_t count);n_write=write(c_fd,returnMsg,strlen(returnMsg));}}while(1){//不斷讀取//5.read ssize_t read(int fd, void *buf, size_t count);memset(readBuf,0,sizeof(readBuf));//不斷清空數據防止數據重復出現n_read=read(c_fd,readBuf,128);if(n_read == -1){perror("read:");}else{printf("得到的消息:%d,%s\n",n_read,readBuf);}}}}return 0; }

客戶端代碼:

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(int argc, char const *argv[]) {int ret;int c_fd;int n_read;int n_write;int c_connect;char readBuf[128];char returnMsg[128]={0};char *quit="quit";struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));//數據清空if(argc != 3){printf("參數出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);c_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協議if(c_fd == -1){printf("創建socket失敗");perror("socket:");exit(-1);}c_addr.sin_family=AF_INET;//ipv4c_addr.sin_port=htons(atoi(argv[2]));//端口號,選擇5000以上。honts返回網絡字節序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&c_addr.sin_addr);//轉換為網絡能識別的格式//2.connect int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);c_connect=connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr));if(c_connect == -1){printf("連接失敗\n");perror("connect:");}while(1){if(fork() == 0){while(1){//不斷寫入memset(returnMsg,0,sizeof(returnMsg));printf("請輸入:\n");gets(returnMsg);//3.write/send ssize_t write(int fd, const void *buf, size_t count);n_write=write(c_fd,returnMsg,strlen(returnMsg));if(strcmp(quit,returnMsg) == 0){//如果輸入quit則客戶端就退出exit(0);}}}while(1){//不斷讀取memset(readBuf,0,sizeof(readBuf));//不斷清空數據防止數據重復出現//4.read ssize_t read(int fd, void *buf, size_t count);n_read=read(c_fd,readBuf,128);if(n_read == -1){perror("read:");}else{printf("來自服務端的消息:%d,%s\n",n_read,readBuf);}}//5.close int close(int fd);close(c_fd);}return 0; }

結果:

多方消息收發

上一節代碼其實已經可以實現多方通信了,不過存在兩個問題:

  • 1、客戶端發消息回車的那一瞬間,光標不知道被哪個進程搶到了,也就是說服務器同一時刻發送的消息,不能確定哪個客戶端子進程收到消息。
  • 2、客戶端之間無法進行互相通訊。

下面的demo加入了類似心跳包的功能,用來說明服務端其實是知道哪個客戶端發來的消息,并且每隔兩秒給每個客戶端回復。

如果想要完全實現類似QQ聊天機制,思路就是將服務端作為中轉站,客戶端和客戶端之間通過服務器完成聊天功能。當然客戶端之間要提前建立“好友”關系,所謂的好友關系可以通過sqlite數據庫存儲每個客戶端的IP、端口、賬號等信息,然后服務端后臺對這些進行邏輯處理,實現客戶端之間的類似QQ聊天機制。

服務端代碼

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(int argc, char const *argv[]) {int mark=0;int s_fd;int c_fd;int n_read;int n_write;char readBuf[128];char returnMsg[128]={0};struct sockaddr_in c_addr;struct sockaddr_in s_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));memset(&s_addr,0,sizeof(struct sockaddr_in));//數據清空if(argc != 3){printf("參數出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);s_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協議if(s_fd == -1){printf("創建socket失敗");perror("socket:");exit(-1);}//2.bind int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);s_addr.sin_family=AF_INET;//ipv4s_addr.sin_port=htons(atoi(argv[2]));//端口號,選擇5000以上。honts返回網絡字節序,atoi(argv[2])防止端口被占用//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&s_addr.sin_addr);//轉換為網絡能識別的格式bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen int listen(int sockfd, int backlog);listen(s_fd,10);//監聽10個連接//4.accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int client=sizeof(struct sockaddr_in);while(1){//不斷接收客戶端c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&client);if(c_fd == -1){printf("連接失敗\n");perror("accept:");exit(-1);}printf("客戶端的ip:%s\n",inet_ntoa(c_addr.sin_addr)); //把網絡格式的ip地址打印成字符串格式mark++;if(fork() == 0){if(fork() == 0){while(1){sprintf(returnMsg,"歡迎第%d號客戶端",mark);//6.write ssize_t write(int fd, const void *buf, size_t count);n_write=write(c_fd,returnMsg,strlen(returnMsg));sleep(20);}}while(1){//5.read ssize_t read(int fd, void *buf, size_t count);memset(readBuf,0,sizeof(readBuf));n_read=read(c_fd,readBuf,128);if(n_read == -1){perror("read:");}else{printf("得到%d號的消息:%s\n",mark,readBuf);}}}}return 0; }

客戶端代碼

#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h>int main(int argc, char const *argv[]) {int c_fd;int n_read;int n_write;int c_connect;char readBuf[128];char returnMsg[128]={0};char *quit="quit";struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));//數據清空if(argc != 3){printf("參數出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);c_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協議if(c_fd == -1){printf("創建socket失敗");perror("socket:");exit(-1);}c_addr.sin_family=AF_INET;//ipv4c_addr.sin_port=htons(atoi(argv[2]));//端口號,選擇5000以上。honts返回網絡字節序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&c_addr.sin_addr);//轉換為網絡能識別的格式//2.connect int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);c_connect=connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr));if(c_connect == -1){printf("連接失敗\n");perror("connect:");}while(1){if(fork() == 0){while(1){//不斷寫入memset(returnMsg,0,sizeof(returnMsg));printf("請輸入:\n");gets(returnMsg);//3.write/send ssize_t write(int fd, const void *buf, size_t count);n_write=write(c_fd,returnMsg,strlen(returnMsg));if(strcmp(quit,returnMsg) == 0){//如果輸入quit則客戶端就退出exit(0);}}}while(1){//不斷讀取memset(readBuf,0,sizeof(readBuf));//不斷清空數據防止數據重復出現//4.read ssize_t read(int fd, void *buf, size_t count);n_read=read(c_fd,readBuf,128);if(n_read == -1){perror("read:");}else{printf("來自服務端的消息:%s\n",readBuf);}}//5.close int close(int fd);close(c_fd);}return 0; }

運行結果:

總結

以上是生活随笔為你收集整理的Linux网络编程(Socket)的全部內容,希望文章能夠幫你解決所遇到的問題。

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