socket編程(2) -- TCP通信

TCP通信

  • 2. 使用 Socket 進行TCP通信
    • 2.1 socket相關函數介紹
    • socket()
    • bind()
    • listen()
    • accept()
    • connect()
    • 2.2 TCP協議 C/S 模型
    • 基礎通信代碼
  • 最后

2. 使用 Socket 進行TCP通信

Socket通信流程圖如下:
在這里插入圖片描述

??這里服務器段listen是監聽socket套接字的監聽文件描述符。如果客戶端有連接請求,服務器端會自動和客戶端建立連接,這里的accept函數,只是從已經建立了連接的已連接隊列中取出一個建立的客戶端連接,并返回用于數據傳輸的文件描述符。

2.1 socket相關函數介紹

socket()

//socket函數
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
返回值:成功返回socket的文件描述符,失敗返回-1,并且通過設置錯誤信息errno
參數:domain:可以選取下面的參數,常用的是AF_INET,AF_INET6,AF_UNIXName                Purpose                          Man pageAF_UNIX, AF_LOCAL   Local communication              unix(7)AF_INET             IPv4 Internet protocols          ip(7)AF_INET6            IPv6 Internet protocols          ipv6(7)AF_IPX              IPX - Novell protocolsAF_NETLINK          Kernel user interface device     netlink(7)type:可以選取下面的參數,常用的是用于tcp通信的SOCK_STREAM,和udp通信的數據包SOCK_DGRAMSOCK_STREAM     Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.SOCK_DGRAM      Supports datagrams (connectionless, unreliable messages of a fixed maximum length).SOCK_SEQPACKET  Provides  a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call.SOCK_RAW        Provides raw network protocol access.SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.SOCK_PACKET     Obsolete and should not be used in new programs; see packet(7).上面的參數還可以或上(|)下面的兩個參數來添加額外屬性:SOCK_NONBLOCK   Set the  O_NONBLOCK file status flag on the new open file description.  Using this flag saves extra calls to fcntl(2) to achieve the same result.SOCK_CLOEXEC   Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor.  See the  description  of  the  O_CLOEXEC flag in open(2) for reasons why this may be useful.protocol: 指定這個socket類型使用的協議,如果這個socket類型只有一個協議,那么這個參數設置為0

bind()

#include <sys/types.h> 
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:成功返回0,失敗返回-1,并設置errno值
參數:sockfd:	使用socket函數成功返回的文件描述符addr:socket地址結構體,這里使用sockaddr_in結構體代替,可以接受的客戶端ip和端口struct sockaddr {sa_family_t sa_family;char        sa_data[14];}struct sockaddr_in {sa_family_t    sin_family; /* address family: AF_INET */in_port_t      sin_port;   /* port in network byte order */struct in_addr sin_addr;   /* internet address */};/* Internet address. */struct in_addr {uint32_t       s_addr;     /* address in network byte order */};addrlen:sockaddr_in結構體大小,sizeof(addr)

listen()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);
返回值:成功返回0,失敗返回-1,并設置errno
參數:sockfd:socket文件描述符,同上backlog:排隊建立3次握手隊列和剛剛建立3次握手隊列的鏈接數和,例如可以設置為1024

accept()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:成功,系統掉用會返回一個非負的整數,這個整數就是已經連接的socket文件描述符,失敗返回-1,并設置errno值。
參數:sockfd:同上addr:傳出參數,取出的這個連接的socket文件描述符的客戶端地址參數,設置為NULL表示不需要傳出addrlen:傳出地址結構體的大小, sizeof(addr),前面為NULL,則它設為NULL

connect()

#include <sys/types.h> 
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:成功返回0,失敗返回-1,并設置errno
參數:sockfd: 客戶端socket文件描述符addr: 傳入參數,指定服務器的地址和端口addrlen: 上面結構體的大小 sizeof(addr)

2.2 TCP協議 C/S 模型

在這里插入圖片描述

為了方便錯誤處理,可以對上面函數進行封裝后使用

//wrap.h
#ifndef __WRAP_H_
#define __WRAP_H_
void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
int Close(int fd);
#endif//wrap.c
#include <wrap.h>void perr_exit(const char *s)
{perror(s);exit(1);
}
// 確定這是一個什么類型的socket,可以接收哪種協議
int Socket(int family, int type, int protocol)
{int sfd;if ((sfd = socket(family, type, protocol)) < 0)perr_exit("socket error");return sfd;
}// 綁定sfd的ip和端口,成功返回0,失敗返回-1
int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{int n;// 成功返回0if ((n = bind(fd, sa, salen)) < 0)perr_exit("bind error");return n;
}// 監聽sfd并自動與連接請求建立連接,監聽成功返回0,失敗返回-1
int Listen(int fd, int backlog)
{int n;if ((n = listen(fd, backlog)) < 0)perr_exit("listen error");return n;
}/* 取出一個已經和服務器sfd的socket建立連接的連接隊列中取出一個客戶端sfd,
后兩個都是傳出參數,是客戶端socket的信息
返回客戶端文件描述符*/
int Accept(int sfd, struct sockaddr *sa, socklen_t *clientsocketlenptr)
{int n;
reaccept:if ((n = accept(sfd, sa, clientsocketlenptr)) < 0){// 防止該阻塞函數被無關的信號打斷if ((errno == ECONNABORTED) || (errno == EINTR))goto reaccept;elseperr_exit("accept error");}return n;
}/*客戶端發起連接,sfd為客戶端socket文件描述符,
后兩個參數是服務器端的ip和端口
連接成功返回0,失敗返回-1*/
int Connect(int sfd, const struct sockaddr *sa, socklen_t salen)
{int n;if ((n = connect(sfd, sa, salen)) < 0)perr_exit("connect error");return n;
}/*從cfd文件描述符中讀取數據到 buf 中
成功,返回讀取到的字符串長度,如果返回0表示讀到末尾,失敗返回-1
*/
ssize_t Read(int cfd, void *buf, size_t buflen)
{ssize_t n;
readagain:if ((n = read(cfd, buf, buflen)) == -1){if (errno == EINTR)goto readagain;elsereturn -1;}else if (n == 0){printf("read end of file\n");}return n;
}ssize_t Write(int cfd, const void *buf, size_t buflen)
{ssize_t n;
writeagain:if ((n = write(cfd, buf, buflen)) == -1){if (errno == EINTR)goto writeagain;elsereturn -1;}else if (n == 0){printf("write end of file\n");}return n;
}int Close(int fd)
{int n;if ((n = close(fd)) == -1)perr_exit("close error");return n;
}

基礎通信代碼

服務器單進程處理客戶端連接和數據通信,主要通過while循環來實現。

//server.c
#include <wrap.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>void showClient(const struct sockaddr_in *clientaddr)
{char buf[16];memset(buf, 0x00, sizeof(buf));inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, buf, sizeof(buf));printf("client family is[%d], ip is[%s] ,port is [%d]----connected\n", clientaddr->sin_family, buf, ntohs(clientaddr->sin_port));
}int main()
{int sfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;bzero(&addr, 0x00);addr.sin_family = AF_INET;addr.sin_port = htons(8888);addr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(sfd, (struct sockaddr *)&addr, sizeof(addr));Listen(sfd, 1024);struct sockaddr_in clientaddr;bzero(&clientaddr, 0x00);int cfd;char buf[64];int n;socklen_t len;while (1){n = 0;len = sizeof(clientaddr);cfd = Accept(sfd, (struct sockaddr *)&clientaddr, &len);showClient(&clientaddr);while (1){memset(buf,0x00,sizeof(buf));n = Read(cfd, buf, sizeof(buf));if (n==0){break;}printf("[%d] byte word,client send say:[%s]\n", n, buf);int i = 0;for (i = 0; i < n; i++){buf[i] = toupper(buf[i]);}Write(cfd, buf, n);}}close(cfd);close(sfd);return 0;
}
//client.c
#include <wrap.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>void showClient(const struct sockaddr_in *clientaddr)
{char buf[16];memset(buf, 0x00, sizeof(buf));inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, buf, sizeof(buf));printf("client family is[%d], ip is[%s] ,port is [%d]----connected\n", clientaddr->sin_family, buf, clientaddr->sin_port);
}int main()
{int cfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;bzero(&addr, 0x00);addr.sin_family = AF_INET;addr.sin_port = htons(8888);inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr);Connect(cfd, (struct sockaddr *)&addr, sizeof(addr));char words[64];int n;while (1){memset(words, 0x00, sizeof(words));// scanf("%s", words);//讀標準輸入數據n = read(STDIN_FILENO, words, sizeof(words));Write(cfd, words, strlen(words));n = Read(cfd, words, sizeof(words));printf("server reply [%s],byte is [%d]\n", words, n);}return 0;
}

最后

推薦一個零聲教育學習教程,個人覺得老師講得不錯,分享給大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,
TCP/IP,協程,DPDK等技術內容,點擊立即學習:鏈接

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

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

相關文章

Redis--布隆過濾器

解決緩存穿透是構建高效緩存系統中的關鍵問題之一。緩存穿透指的是惡意或者非法請求經過緩存層直接訪問數據庫或者后端服務&#xff0c;導致系統資源浪費和性能下降的情況。為了有效應對緩存穿透問題&#xff0c;以下是幾種常見的解決方法&#xff1a; 1. 布隆過濾器預檢查 布…

運維-Docker-黑馬

運維-Docker-黑馬 編輯時間&#xff1a;2024/7/15 來源&#xff1a;黑馬程序員 docker&#xff1a;快速構建&#xff0c;運行&#xff0c;管理應用的工具 Docker安裝 部署mysql 命令解讀

[Cesium for Supermap] 加載3dTiles,點擊獲取屬性

代碼&#xff1a; // 設為橢球var obj [6378137.0, 6378137.0, 6356752.3142451793];Cesium.Ellipsoid.WGS84 Object.freeze(new Cesium.Ellipsoid(obj[0], obj[1], obj[2]));var viewer new Cesium.Viewer(cesiumContainer);var scene viewer.scenescene.lightSource.ambi…

Oracle TDE(Transparent Data Encryption) 常見問題解答 - 官網

此FAQ來源于官網鏈接。此為新版&#xff0c;老版的博客參見Oracle TDE(Transparent Data Encryption) 常見問題解答。 通用問題 透明數據加密 (TDE) 提供什么功能&#xff1f; TDE 以透明方式加密 Oracle 數據庫中的靜態數據。它可以阻止操作系統未經授權嘗試訪問存儲在文件…

徹底改變時尚:使用 GAN 實現 AI 的未來

徹底改變時尚&#xff1a;使用 GAN 實現 AI 的未來 一、介紹 想象一下&#xff0c;在這個世界里&#xff0c;時裝設計師永遠不會用完新想法&#xff0c;我們穿的每一件衣服都是一件藝術品。聽起來很有趣&#xff0c;對吧&#xff1f;好吧&#xff0c;我們可以在通用對抗網絡 &a…

鴻蒙基本工程目錄

工程級目錄 AppScope 中存放應用全局所需要的資源文件。entry 是應用的主模塊&#xff0c;存放 HarmonyOS 應用的代碼、資源等。oh_modules 是工程的依賴包&#xff0c;存放工程依賴的源文件。build-profile.json5 是工程級配置信息&#xff0c;包括簽名、產品配置等。hvigorf…

品牌產業出海指南如何搭建國際化架構的跨境電商平臺?

在“品牌&產業出海指南 – 成功搭建跨境電商平臺”系列中&#xff0c;我們將從電商分銷系統、跨境平臺商城/多商戶商城系統和國際化架構三個方面對幫助您梳理不同平臺模式的優缺點、應用場景、開發重點和運營建議。 在“品牌&產業出海指南 – 成功搭建跨境電商平臺”系…

【漏洞復現】Rejetto HTTP文件服務器——遠程命令執行(CVE-2024-23692)

聲明&#xff1a;本文檔或演示材料僅供教育和教學目的使用&#xff0c;任何個人或組織使用本文檔中的信息進行非法活動&#xff0c;均與本文檔的作者或發布者無關。 文章目錄 漏洞描述漏洞復現測試工具 漏洞描述 Rejetto HTTP文件服務器是一個輕量級的HTTP服務器軟件&#xff…

VBA學習(20):一批簡單的Excel VBA編程問題解答

1.如何確定單元格區域內的行數和列數&#xff1f; 使用Range.Rows.Count和Range.Columns.Count屬性。 2.Application.Columns指的是什么&#xff1f; 活動工作表中的列。 3.你的程序在列B位置插入一個新列&#xff0c;原來的列B會怎樣&#xff1f; 它向右移動成為列C。 4.假…

vue項目1分鐘實現自定義右鍵菜單,懶人的福音

高效實現需求&#xff0c;避免重復造輪子&#xff0c;今天給大家分享的是&#xff0c;如何在最短的時間內實現右鍵菜單&#xff0c;方法也很簡單&#xff0c;一個插件就可以搞定&#xff0c;話不多說&#xff0c;上效果圖&#xff1a; 1. 效果圖&#xff1a; 2. 安裝&#xff…

5. 基于Embedding實現超越elasticsearch高級搜索

Embedding介紹 Embedding是向量的意思&#xff0c;向量可以理解為平面坐標中的一個坐標點(x,y),在編程領域&#xff0c;一個二維向量就是一個大小為float類型的數組。也可以用三維坐標系中的向量表示一個空間中的點。在機器學習中&#xff0c;向量通常用于表示數據的特征。 向量…

SCI丨中三區

無線網絡遙感圖像和視頻處理技術在xxxxx析基于智能物聯網的xxxxx養老模式可持續發展基于心理行為大數據分類算法xxxxxx研究基于云計算xxxxx行為分析及客戶感知體系的構建基于機器學習的xxxxx金鋼時效行為研究 基于機器視覺的xxxxx檢測系統研究 機器學習的電子顯微鏡xxxxx材料的…

探索Laravel的視圖組件與插槽:構建動態且可復用的UI

探索Laravel的視圖組件與插槽&#xff1a;構建動態且可復用的UI 引言 Laravel作為一個現代化的PHP框架&#xff0c;提供了許多強大的功能來幫助開發者構建高性能和可維護的Web應用。其中&#xff0c;視圖組件&#xff08;View Components&#xff09;和插槽&#xff08;Slots…

【React Hooks原理 - forwardRef、useImperativeHandle】

概述 上文我們聊了useRef的使用和實現&#xff0c;主要兩個用途&#xff1a;1、用于持久化保存 2、用于綁定dom。 但是有時候我們需要在父組件中訪問子組件的dom或者屬性/方法&#xff0c;而React中默認是不允許父組件直接訪問子組件的dom的&#xff0c;這時候就可以通過forwa…

數據庫SQL Server列拼接Join和Union

文章目錄 JOINJOIN的基本語法如下&#xff1a; UNIONUNION的基本語法如下&#xff1a; 在 SQL Server中&#xff0c; JOIN和 UNION是兩種不同的操作&#xff0c;它們用于合并來自兩個或多個表的數據。 JOIN JOIN操作用于將兩個或多個表中的行結合起來&#xff0c;基于它們之…

Jmeter二次開發Demo

Jmeter二次開發Demo 前言 在上一集&#xff0c;我們已經完成了JMX腳本的分析&#xff0c;大致了解了JMX腳本的基本元素。 那么在這一集&#xff0c;我們將會介紹一下Jmeter二次開發的Demo。 Demo代碼 那么話不多說&#xff0c;我們就直接上代碼。 public class TestStress…

SpringBoot+HttpClient實現文件上傳下載

服務端&#xff1a;SpringBoot Controller package com.liliwei.controller;import java.io.File; import java.io.FileInputStream; import java.io.IOException;import javax.servlet.http.HttpServletResponse;import org.springframework.http.HttpHeaders; import org.s…

Cesium 判斷位置是否在當前視口范圍內

詳細步驟都在注釋里,不過多贅述了。 /*** @param {Object} position - Cartesian3坐標* @return {Boolean} 是否在視口中*/ function isPositionInViewport(position) {// 獲取當前視口范圍let viewport = viewer.camera.computeViewRectangle();// 2D模式下拾取不到坐標,vi…

類和對象的簡述(c++篇)

開局之前&#xff0c;先來個小插曲&#xff0c;放松一下&#xff1a; 讓我們的熊二來消滅所有bug 各位&#xff0c;在這祝我們&#xff1a; 放松過后&#xff0c;開始步入正軌吧。愛學習的鐵子們&#xff1a; 目錄&#xff1a; 一類的定義&#xff1a; 1.簡述&#xff1a; 2…

【JavaScript 算法】貪心算法:局部最優解的構建

&#x1f525; 個人主頁&#xff1a;空白詩 文章目錄 一、貪心算法的基本概念貪心算法的適用場景 二、經典問題及其 JavaScript 實現1. 零錢兌換問題2. 活動選擇問題3. 分配問題 三、貪心算法的應用四、總結 貪心算法&#xff08;Greedy Algorithm&#xff09;是一種逐步構建解…