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

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux网络编程(Socket)

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

目錄

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

網(wǎng)絡(luò)編程(Socket)概述

引入

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

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

網(wǎng)絡(luò)編程通識掃盲


socket套接字

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

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

套接字描述符

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

字節(jié)序

字節(jié)序就是字節(jié)存儲的順序(從高地址開始存儲還是從低地址開始存儲),在網(wǎng)絡(luò)編程中要注意相關(guān)協(xié)議使用的字節(jié)序,防止數(shù)據(jù)傳輸出錯。具體使用的是字節(jié)序轉(zhuǎn)換api,配合端口號使用

socket編程步驟

模擬場景:

步驟介紹:

Linux提供的API簡析

創(chuàng)建套接字即連接協(xié)議[socket](服、客)

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

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

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

struct sockaddr 這個結(jié)構(gòu)體一般同等替代成struct sockaddr_in,使用的時候注意新結(jié)構(gòu)體的類型強制轉(zhuǎn)換。

地址轉(zhuǎn)換api

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

字節(jié)序轉(zhuǎn)換api

監(jiān)聽[listen](服)

int listen(int sockfd, int backlog);

backlog:支持最大的連接數(shù)

接受連接[accept](服)

三次握手成功就建立accept連接。函數(shù)里面的結(jié)構(gòu)體存放客戶端的IP和端口號等信息!

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

數(shù)據(jù)收發(fā)[read、write](服、客)

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

數(shù)據(jù)收發(fā)第二套API,多了flags控制參數(shù)

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

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

socket服務(wù)端代碼實現(xiàn)

利用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="我收到了你的信息";//發(fā)送給客戶端的消息 盡量不使用數(shù)組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));//數(shù)據(jù)清空 再配置//1.socket int socket(int domain, int type, int protocol);s_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協(xié)議if(s_fd == -1){printf("創(chuàng)建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以上(有些端口被系統(tǒng)調(diào)用)。honts返回網(wǎng)絡(luò)字節(jié)序//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是結(jié)構(gòu)體sockaddr_in里面的結(jié)構(gòu)體 存放IP(下面有查找到原型) 然后轉(zhuǎn)換為網(wǎng)絡(luò)能識別的格式//或者使用ifconfig命令查到實際本機的IP也可以bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//結(jié)構(gòu)體類型轉(zhuǎn)換,因為用的同等替換的結(jié)構(gòu)體sockaddr_in//3.listen int listen(int sockfd, int backlog);listen(s_fd,10);//監(jiān)聽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進行類型強轉(zhuǎn) 存放客戶端信息if(c_fd == -1){printf("連接失敗\n");perror("accept:");exit(-1);}//客戶端printf("客戶端的ip:%s\n",inet_ntoa(c_addr.sin_addr)); //把網(wǎng)絡(luò)格式的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; }

運行結(jié)果(客戶端發(fā)送 huai dan):

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

進去就能找到這個結(jié)構(gòu)體原型啦:

socket客戶端代碼實現(xiàn)

客戶端向服務(wù)端發(fā)送消息,實現(xiàn)通信(只能進行一次通訊)。

#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));//數(shù)據(jù)清空//1.socket int socket(int domain, int type, int protocol);c_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協(xié)議if(c_fd == -1){printf("創(chuàng)建socket失敗");perror("socket:");exit(-1);}c_addr.sin_family=AF_INET;//ipv4c_addr.sin_port=htons(8687);//端口號,選擇5000以上。honts返回網(wǎng)絡(luò)字節(jié)序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton("192.168.103.49",&c_addr.sin_addr);//轉(zhuǎn)換為網(wǎng)絡(luò)能識別的格式//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的使用區(qū)別//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("來自服務(wù)端的消息:%d,%s\n",n_read,readBuf);}//5.close int close(int fd);close(c_fd);return 0; }

運行結(jié)果:

實現(xiàn)雙方(多方)一直聊天

本質(zhì)就是上面的代碼加入while循環(huán),實現(xiàn)不斷的消息收發(fā)

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

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

客戶端僅用fork創(chuàng)建了一個進程,同樣應(yīng)用在“寫”,“讀”放在while循環(huán)中,這樣就實現(xiàn)了讀和寫并行運行。

服務(wù)端代碼:

#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));//數(shù)據(jù)清空if(argc != 3){printf("參數(shù)出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);s_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協(xié)議if(s_fd == -1){printf("創(chuàng)建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返回網(wǎng)絡(luò)字節(jié)序,atoi(argv[2])防止端口被占用//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&s_addr.sin_addr);//轉(zhuǎn)換為網(wǎng)絡(luò)能識別的格式bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen int listen(int sockfd, int backlog);listen(s_fd,10);//監(jiān)聽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)); //把網(wǎng)絡(luò)格式的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));//不斷清空數(shù)據(jù)防止數(shù)據(jù)重復出現(xiàn)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));//數(shù)據(jù)清空if(argc != 3){printf("參數(shù)出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);c_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協(xié)議if(c_fd == -1){printf("創(chuàng)建socket失敗");perror("socket:");exit(-1);}c_addr.sin_family=AF_INET;//ipv4c_addr.sin_port=htons(atoi(argv[2]));//端口號,選擇5000以上。honts返回網(wǎng)絡(luò)字節(jié)序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&c_addr.sin_addr);//轉(zhuǎn)換為網(wǎng)絡(luò)能識別的格式//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));//不斷清空數(shù)據(jù)防止數(shù)據(jù)重復出現(xiàn)//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("來自服務(wù)端的消息:%d,%s\n",n_read,readBuf);}}//5.close int close(int fd);close(c_fd);}return 0; }

結(jié)果:

多方消息收發(fā)

上一節(jié)代碼其實已經(jīng)可以實現(xiàn)多方通信了,不過存在兩個問題:

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

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

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

服務(wù)端代碼

#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));//數(shù)據(jù)清空if(argc != 3){printf("參數(shù)出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);s_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協(xié)議if(s_fd == -1){printf("創(chuàng)建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返回網(wǎng)絡(luò)字節(jié)序,atoi(argv[2])防止端口被占用//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&s_addr.sin_addr);//轉(zhuǎn)換為網(wǎng)絡(luò)能識別的格式bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3.listen int listen(int sockfd, int backlog);listen(s_fd,10);//監(jiān)聽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)); //把網(wǎng)絡(luò)格式的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));//數(shù)據(jù)清空if(argc != 3){printf("參數(shù)出錯\n");exit(-1);}//1.socket int socket(int domain, int type, int protocol);c_fd=socket(AF_INET,SOCK_STREAM,0);//ipv4 tcp協(xié)議if(c_fd == -1){printf("創(chuàng)建socket失敗");perror("socket:");exit(-1);}c_addr.sin_family=AF_INET;//ipv4c_addr.sin_port=htons(atoi(argv[2]));//端口號,選擇5000以上。honts返回網(wǎng)絡(luò)字節(jié)序//int inet_aton(const char *cp, struct in_addr *inp)inet_aton(argv[1],&c_addr.sin_addr);//轉(zhuǎn)換為網(wǎng)絡(luò)能識別的格式//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));//不斷清空數(shù)據(jù)防止數(shù)據(jù)重復出現(xiàn)//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("來自服務(wù)端的消息:%s\n",readBuf);}}//5.close int close(int fd);close(c_fd);}return 0; }

運行結(jié)果:

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。