網絡編程,使用select()進行簡單服務端與客戶端通信

這里在Ubuntu環境下演示

一般流程

服務端常用函數:

  1. socket():創建一個新的套接字。
  2. bind():將套接字與特定的IP地址和端口綁定。
  3. listen():使套接字開始監聽傳入的連接請求。
  4. accept():接受一個傳入的連接請求,并創建一個新的套接字用于與客戶端通信。
  5. send()?或?write():向連接的客戶端發送數據。
  6. recv()?或?read():接收來自客戶端的數據。
  7. close():關閉與客戶端通信的套接字。

流程通常是:

socket() -> bind() -> listen() -> accept() -> send()或write() / recv()或read() -> close() 

客戶端常用函數:

  1. socket():創建一個新的套接字。
  2. connect():嘗試與服務端建立連接。
  3. send()?或?write():向服務端發送數據。
  4. recv()?或?read():接收來自服務端的數據。
  5. close():關閉與服務端通信的套接字。

流程通常是:

 socket() -> connect ->  send()或write() / recv()或read() -> close() 

簡單函數介紹

socket()

socket用于創建一個套接字。

所謂套接字(Socket),就是對網絡中不同主機上的應用進程之間進行雙向通信的端點的抽象。一個套接字就是網絡上進程通信的一端,提供了應用層進程利用網絡協議交換數據的機制。從所處的地位來講,套接字上聯應用進程,下聯網絡協議棧,是應用程序通過網絡協議進行通信的接口,是應用程序與網絡協議棧進行交互的接口

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????——百度百科

Linux中socket的返回類型是整形,Windows中返回的是SOCKET類型。

    int socket(int af,int type,int protocol);//windows中的返回類型是SOCKET

socket的三個參數:

? ?//例如一次初始化:

? ?int s = socket(AF_INET, SOCK_STREAM, 0);

? ?

? ? //三個參數:
? ? /*
? ? ? ? (1)地址族:
? ? ?* AF_INET表示使用IPv4(Internet Protocol version 4)地址。
? ? ?* 它指定了套接字將在IPv4網絡上使用。
? ? ?* 與之對應的是AF_INET6,用于IPv6地址。
? ? ? ? (2)套接字類型(Socket Type):
? ? ?* SOCK_STREAM表示套接字是一個面向連接的、可靠的、基于字節流的套接字,通常用于TCP(Transmission Control Protocol)連接。
? ? ?* 與之對應的是SOCK_DGRAM,表示一個無連接的、固定最大長度消息、不可靠的套接字,通常用于UDP(User Datagram Protocol)連接。
? ? ? ? (3)協議:
? ? ?* 這個參數通常設置為0,表示讓系統自動選擇適合指定地址族和套接字類型的協議。
? ? ?* 在大多數情況下,對于AF_INET和SOCK_STREAM的組合,系統會選擇TCP協議。
? ? ?* 同樣地,對于AF_INET和SOCK_DGRAM的組合,系統會選擇UDP協議。
? ? ?*/

connect()

connect 函數用于建立客戶端和服務器之間的連接。

它屬于套接字編程接口,用于將一個套接字與遠程服務器的特定套接字地址關聯起來,從而初始化一個連接。

connect函數原型

int connect(SOCKET s,const struct sockaddr *name,int namelen);

其中,struct sockaddr的類型:

    struct sockaddr {  sa_family_t sin_family;//地址族char sa_data[14];             }; 

這個類型需要獲取到服務器的IP與端口,但是不好進行操作,所以我們要用到跟其作用一致的結構體——struct sockaddr_in:

struct sockaddr_in
{sa_family_t sin_family;//地址族uint16_t sin_port;  //端口struct in_addr sin_addr;  //32位IPchar sin_zero[8];   
}

? ?//例如,對struct sockaddr_in進行一次初始化

????struct sockaddr_in server_addr;


? ? //給該結構體清空
? ? memset(&server_addr, 0, sizeof(server_addr));
? ? //初始化,要拿到主機的ip和端口
? ? server_addr.sin_family = AF_INET;
? ? server_addr.sin_port = htons(port);
? ? server_addr.sin_addr.s_addr = inet_addr(ip);

? ?

那么connect傳參類似如下:

if (connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
? ? ? ? perror("Connect failed");
? ? ? ? // 連接失敗
? ? }

send()

send()?是一個用于通過套接字(socket)發送數據的核心函數

send()函數原型:

int send(SOCKET s,const char *buf,int len,int flags);

其中

?//——最后一個參數flags用于控制信息發送方式,這里0是默認發送方式
?//send返回的是實際發送的字節數,失敗則返回-1

簡單客戶端實現代碼

客戶端實現代碼:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>int tcp_echo_client_start(const char *ip, int port)
{printf("tcp echo client, ip: %s, port:%d\n", ip, port);int s = socket(AF_INET, SOCK_STREAM, 0);//1、創建套接字//如果創建套接字失敗if(s < 0){perror("tcp echo client: open socket error");return -1;}//2、用connect函數向服務器建立連接struct sockaddr_in server_addr;//給該結構體清空memset(&server_addr, 0, sizeof(server_addr));//初始化,要拿到主機的ip和端口server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);server_addr.sin_addr.s_addr = inet_addr(ip);if (connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("Connect failed");// 處理錯誤}//3、從鍵盤中讀取輸入,并發送數據char buf[128];printf("please input:");while(fgets(buf, sizeof(buf), stdin) != NULL){//發送,發送給套接字就是交給操作系統去管理if(send(s, buf, strlen(buf), 0) < 0){perror("write error");close(s);return -1;}//服務端收到消息會響應,需要接受數據memset(buf, 0, sizeof(buf));int len = recv(s, buf, sizeof(buf) - 1, 0);if(len < 0)perror("read error");//接受數據并打印printf("%s\n", buf);printf(">>\n");}//關閉close(s);//Windows中采用closesocket()函數return 0;
}int main()
{tcp_echo_client_start("192.168.74.1", 8080);return 0;
}

服務端實現

bind()

bind()?函數用于將一個?套接字(socket)?綁定到特定的?IP 地址和端口號,通常用于服務器端設置監聽地址

bind()函數原型:

int bind(int sockfd,                     // 套接字文件描述符(由 socket() 創建)const struct sockaddr *addr,    // 指向 sockaddr 結構體的指針(存儲地址信息)socklen_t addrlen               // sockaddr 結構體的長度
);

//成功返回0,失敗返回-1

sockfd:

  • 由?socket()?創建的套接字描述符(如?int sockfd = socket(AF_INET, SOCK_STREAM, 0);)。

addr:

  • 指向?struct sockaddr?的指針,存儲?IP 地址 + 端口號

  • 實際使用時通常用?struct sockaddr_in(IPv4)或?struct sockaddr_in6(IPv6),并強制轉換為?sockaddr*

listen()

listen()?函數用于將?TCP 套接字?設置為?被動監聽模式,等待客戶端發起連接請求(connect()

#include <sys/socket.h>int listen(int sockfd,     // 已綁定地址的套接字描述符(由 socket() 創建 + bind() 綁定)int backlog     // 允許排隊的最大未完成連接數(直接影響并發處理能力)
);

//成功返回0,失敗返回-1

sockfd:

  • 必須是已通過?bind()?綁定到某個?IP + 端口?的?流式套接字(如?SOCK_STREAM,即 TCP 套接字)。

  • 如果未綁定,系統會隨機分配一個端口(但服務器通常需要固定端口,所以必須顯式?bind())。

backlog:

  • 定義?已完成三次握手但未被?accept()?取走的連接隊列的最大長度(即“等待處理的連接”)。

  • 不同操作系統對?backlog?的實現有差異,但通常遵循以下規則:

    • Linux:實際隊列長度 =?min(backlog, /proc/sys/net/core/somaxconn)(默認值通常為?128)。

    • Windows:直接使用?backlog,但最大值由系統限制。

    • 推薦值

      • 高并發服務器:128?或更高(需調整系統參數?somaxconn)。

      • 簡單測試:5~10

accept()

accept()?函數用于?從已監聽的 TCP 套接字中接受一個客戶端的連接請求,并返回一個新的套接字描述符

#include <sys/socket.h>//成功:返回一個新的 套接字描述符(int),專門用于與客戶端通信。
//失敗:返回-1
int accept(int sockfd,                     // 已調用 listen() 的監聽套接字struct sockaddr *addr,          // 用于存儲客戶端地址信息(可選,可設為 NULL)socklen_t *addrlen              // 客戶端地址結構體的長度(輸入輸出參數)
);

服務端

在Ubuntu中實現的:

#include <sys/socket.h>
#include <error.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>  // 包含close函數的聲明
#include <pthread.h>
#include <sys/poll.h>void *client_thread(void *arg)
{int clientfd = *(int *)arg;while(1){char buffer[128] = {0};int count = recv(clientfd, buffer, 128, 0);  if(count == 0){break;}send(clientfd, buffer, count, 0); // 只發送接收到的數據長度printf("clientfd: %d, count: %d, buffer:%s\n", clientfd, count, buffer);  }close(clientfd);  //關閉return NULL;
}int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);//綁定IPstruct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(struct sockaddr_in));serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons(8080);// 使用 `bind` 函數將套接字綁定到指定的地址和端口上。如果綁定失敗,程序將打印錯誤信息并退出。if(-1 == bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr))){perror("bind");return -1;}listen(sockfd, 10);    //使用 `listen` 函數使套接字進入監聽狀態,等待傳入連接,隊列長度設置為 10。fd_set rfds, rset;FD_ZERO(&rfds);    //將其清空FD_SET(sockfd, &rfds);int maxfd = sockfd;while(1) {rset = rfds;int nready = select(maxfd + 1, &rset, NULL, NULL, NULL);//timeout參數設置為NULL,就是一直等待if(FD_ISSET(sockfd, &rset)){struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);printf("accept\n");FD_SET(clientfd, &rfds);if(clientfd > maxfd)maxfd = clientfd;}for(int i = 0; i <= maxfd; i++){if(FD_ISSET(i, &rset) && i != sockfd){//讀char buffer[128] = {0};int count = recv(i, buffer, 128, 0);  if(count == 0){printf("disconnect\n");FD_CLR(i, &rfds);close(i);}else{send(i, buffer, count, 0);printf("clientfd: %d, count: %d, buffer:%s\n", i, count, buffer);  }}}}getchar();  // 等待用戶輸入以便保持程序運行  return 0;
}

演示效果

先編譯:

運行server端后,可以查看8080端口,查看其狀態

netstat -tulnp | grep 8080

然后在客戶端中連接服務端:

在客戶端這邊使用鍵盤輸入:

客戶端斷開后,服務端提示:

不過服務端依舊在LISTEN:

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

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

相關文章

智能決策支持系統的基本概念與理論體系

決策支持系統是管理科學的一個分支&#xff0c;原本與人工智能屬于不同的學科范疇&#xff0c;但自20世紀80年代以來&#xff0c;由于專家系統在許多方面取得了成功&#xff0c;于是人們開始考慮把人工智能技術用于計算機管理中來。在用計算機所進行的各種管理中&#xff0c;如…

驅動開發系列55 - Linux Graphics QXL顯卡驅動代碼分析(二)顯存管理

一:概述 前面介紹了當內核檢測到匹配的PCI設備后,會調用 qxl_pci_probe 初始化設備,其中會調用qxl_device_init 來初始化設備,為QXL設備進行內存映射,資源分配,環形緩沖區初始化,IRQ注冊等操作,本文展開說說這些細節,以及介紹下QXL的顯存管理。 二:QXL設備初始化細節…

洛谷 P1495:【模板】中國剩余定理(CRT)/ 曹沖養豬

【題目來源】 https://www.luogu.com.cn/problem/P1495 https://www.acwing.com/problem/content/225/ 【題目描述】 自從曹沖搞定了大象以后&#xff0c;曹操就開始捉摸讓兒子干些事業&#xff0c;于是派他到中原養豬場養豬。可是曹沖滿不高興&#xff0c;于是在工作中馬馬虎…

配置和使用持久卷

配置和使用持久卷 文章目錄 配置和使用持久卷[toc]一、PV與PVC的持久化存儲機制二、PV和PVC的生命周期三、創建基于NFS的PV1.準備NFS共享目錄2.創建PV 四、基于PVC使用PV1.創建PVC2.使用PVC 五、基于StorageClass實現動態卷制備1.獲取NFS服務器的連接信息2.獲取nfs-subdir-exte…

FreeRTOS菜鳥入門(十)·消息隊列

目錄 1. 基本概念 2. 數據存儲 3. 運作機制 4. 阻塞機制 4.1 出隊阻塞 4.2 入隊阻塞 5. 操作示意圖 5.1 創建隊列 5.2 向隊列發送第一個消息 5.3 向隊列發送第二個消息 5.4 從隊列讀取消息 6. 消息隊列控制塊 7. 消息隊列常用函數 7.1 消息隊列創建…

java 洛谷題單【算法2-2】常見優化技巧

P1102 A-B 數對 解題思路 輸入讀取與初始化&#xff1a; 使用 Scanner 讀取輸入。n 表示數組的長度&#xff0c;c 表示目標差值。使用一個 HashMap 存儲數組中每個數字及其出現的次數&#xff0c;方便快速查找。數組 a 用于存儲輸入的數字。 構建哈希映射&#xff1a; 遍歷數…

視頻轉GIF

視頻轉GIF 以下是一個使用 Python 將視頻轉換為 GIF 的腳本&#xff0c;使用了 imageio 和 opencv-python 庫&#xff1a; import cv2 import imageio import numpy as np """將視頻轉換為GIF圖參數:video_path -- 輸入視頻的路徑gif_path -- 輸出GIF的路徑fp…

計算機網絡:詳解TCP協議(四次握手三次揮手)

目錄 1.Tcp協議介紹 1.1 Tcp協議層級 1.2 TCP協議的格式 2. 確認應答機制 2.1 確認應答 2.2 序號字段 2.3 捎帶應答 3. 流量控制 4. 三次握手 四次揮手 4.1 認識標志位 4.2 簡單認識 4.3 三次揮手 4.4 四次揮手 1.Tcp協議介紹 1.1 Tcp協議層級 計算機網絡&#x…

小程序 IView WeappUI組件庫(簡單增刪改查)

IView Weapp 微信小程序UI組件庫&#xff1a;https://weapp.iviewui.com/components/card IView Weapp.png 快速上手搭建 快速上手.png iView Weapp 的代碼 將源代碼下載下來&#xff0c;然后將dict放到自己的項目中去。 iView Weapp 的代碼.png 小程序中添加iView Weapp 將di…

用java實現一個簡單的sql select 解析器,無需第三方依賴,完全從0開始

以下是一個簡單的 SQL SELECT 解析器的 Java 實現&#xff0c;支持單表查詢和基本條件過濾。代碼包含詞法分析和語法分析模塊&#xff0c;并支持以下語法&#xff1a; SELECT column1, column2 FROM table WHERE column3 5 完整代碼 1. Token 類型定義 (TokenType.java) pu…

阿里云 CentOS YUM 源配置指南

阿里云 CentOS YUM 源配置指南 在使用 CentOS 7 時&#xff0c;由于 CentOS 官方源停止維護等原因&#xff0c;yum install 命令可能會報錯 “Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64”。以下是通過更換阿里云源解決該問題的詳細步驟。 一、備份原有配…

Learning vtkjs之ThresholdPoints

過濾器 閾值過濾器 介紹 vtkThresholdPoints - 提取滿足閾值條件的點 vtkThresholdPoints 是一個過濾器&#xff0c;它從數據集中提取滿足閾值條件的點。該條件可以采用三種形式&#xff1a; 1&#xff09;大于特定值&#xff1b; 2) 小于特定值&#xff1b; 3) 在特定值之間…

記錄ruoyi-flowable-plus第一次運行流程報錯

記錄ruoyi-flowable-plus第一次運行流程報錯 錯誤步驟 1.啟動ruoyi-flowable-plus 正常登錄后&#xff0c;打開流程分類然后點擊新增按鈕&#xff0c;新增了一個分類。增加成功后&#xff0c; 再點擊流程分類&#xff0c;報錯。 錯誤提示 org.springframework.cglib.core.C…

Java中的stream流介紹與使用

一、Stream 的基礎概念 定義與特性 Stream 是單向數據流&#xff0c;對集合或數組進行高效處理&#xff0c;不存儲數據&#xff0c;而是通過操作鏈生成新 Stream。不可變性&#xff1a;原始數據源不被修改&#xff0c;所有操作均返回新 Stream。延遲執行&#xff1a;中間操作&a…

OCR身份證識別(正反面)_個人證照OCR識別_開放API接口使用指南

一、接口簡介 在數字化時代&#xff0c;快速準確地提取身份證信息變得尤為重要。**萬維易源提供的“身份證OCR識別”API接口&#xff0c;能夠快速提取二代居民身份證正反面的所有字段信息&#xff0c;包括姓名、性別、民族、出生日期、住址、身份證號、簽發機關、有效期限等。…

25年新版潮乎盲盒系統源碼 盲盒商城系統前端分享

盲盒系統市場的前景一直都很不錯&#xff0c;最近很多問我有沒有盲盒源碼的客戶&#xff0c;下面給大家分享一個新版潮乎盲盒源碼&#xff01; 這款盲盒源碼系統 前端Uniapp 后端使用了Laravel框架進行開發。Laravel是一個流行的PHP框架&#xff0c;具有強大的功能和易于使用的…

Transformer四模型回歸打包(內含NRBO-Transformer-GRU、Transformer-GRU、Transformer、GRU模型)

Transformer四模型回歸打包&#xff08;內含NRBO-Transformer-GRU、Transformer-GRU、Transformer、GRU模型&#xff09; 目錄 Transformer四模型回歸打包&#xff08;內含NRBO-Transformer-GRU、Transformer-GRU、Transformer、GRU模型&#xff09;預測效果基本介紹程序設計參…

Axure疑難雜癥:利用中繼器制作三級下拉菜單(邏輯判斷進階)

親愛的小伙伴,在您瀏覽之前,煩請關注一下,在此深表感謝! Axure產品經理精品視頻課已登錄CSDN可點擊學習https://edu.csdn.net/course/detail/40420 課程主題:三級下拉菜單 主要內容:條件篩選時的邏輯判斷思維,中繼器使用 應用場景:復合條件下的下拉列表制作 案例展…

Nginx 核心功能之正反代理

目錄 一、Nginx 二、正向代理 三、反向代理 四、Nginx 緩存 1. 緩存功能的核心原理和緩存類型 2. 代理緩存功能設置 五、Nginx rewrite和正則 &#xff08;1&#xff09;Nginx 正則 &#xff08;2&#xff09;nginx location &#xff08;3&#xff09;Rewrite &…

ssh連接云服務器記錄

文章目錄 1. 背景2. ssh連接2.1 win 下通過終端工具進行連接2.2 Linux下通過ssh指令連接2.3 ssh使用publickey來連接 ssh連接云服務器記錄 1. 背景 最近開始接觸docker技術、mysql技術&#xff0c;加上本人工作基本都在Linux下進行&#xff0c;因此需要一套Linux環境進行練習。…