Linux 編程之 Socket

2021-12-23 Linux愛好者

(點擊上方公眾號,可快速關注)

英文:hxf0223   

連結:http://blog.chinaunix.net/uid-670184-id-5754181.html

TCP/IP協議及socket封裝


socket編程的基本流程



socket連接的建立(3次握手)




socket連接的斷開(3次握手)


由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

(1)客戶端A發送一個FIN,用來關閉客戶A到伺服器B的數據傳送(報文段4)。

(2)伺服器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。

(3)伺服器B關閉與客戶端A的連接,發送一個FIN給客戶端A(報文段6)。

(4)客戶端A發回ACK報文確認,並將確認序號設置為收到序號加1(報文段7)。

對應函數接口如圖:

socket編程之bind函數

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

功能描述:

當用socket()函數創建套接字以後,套接字在名稱空間(網絡地址族)中存在,但沒有任何地址給它賦值。bind()把用addr指定的地址賦值給用文件描述符代表的套接字sockfd。addrlen指定了以addr所指向的地址結構體的字節長度。一般來說,該操作稱為「給套接字命名」。

通常,在一個SOCK_STREAM套接字接收連接之前,必須通過bind()函數用本地地址為套接字命名。

備註:

調用bind()函數之後,為socket()函數創建的套接字關聯一個相應地址,發送到這個地址的數據可以通過該套接字讀取與使用。


備註:


bind()函數並不是總是需要調用的,只有用戶進程想與一個具體的地址或埠相關聯的時候才需要調用這個函數。如果用戶進程沒有這個需要,那麼程序可以依賴內核的自動的選址機制來完成自動地址選擇,而不需要調用bind()函數,同時也避免不必要的複雜度。在一般情況下,對於伺服器進程問題需要調用bind()函數,對於客戶進程則不需要調用bind()函數。

socket編程之accept函數

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

功能參數描述

accept()系統調用主要用在基於連接的套接字類型,比如SOCK_STREAM和SOCK_SEQPACKET。它提取出所監聽套接字的等待連接隊列中第一個連接請求,創建一個新的套接字,並返回指向該套接字的文件描述符。新建立的套接字不在監聽狀態,原來所監聽的套接字也不受該系統調用的影響。

備註:


新建立的套接字準備發送send()和接收數據recv()。

參數:

sockfd,    利用系統調用socket()建立的套接字描述符,通過bind()綁定到一個本地地址(一般為伺服器的套接字),並且通過listen()一直在監聽連接;

伺服器代碼:

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <string.h>

#include <fcntl.h>

#include <sys/shm.h>

 

#define QUEUE        20

#define MYPORT        8887

#define BUFF_SIZE    1024

 

int main(int argc, char *argv[])

{

    int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);    // 定義socket fd

 

    // 定義sockaddr_in

    struct sockaddr_in server_sockaddr;

    server_sockaddr.sin_family = AF_INET;

    server_sockaddr.sin_port = htons(MYPORT);            // host to network short

    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);// host to network long

 

    // bind socket address to socket fd

    if (bind(server_sockfd, (struct sockaddr*)&server_sockaddr, sizeof(server_sockaddr)) == -1) {

        perror("bind");

        exit(1);

    }

 

    if (listen(server_sockfd, QUEUE) == -1) {

        perror("listen");

        exit(2);

    }

 

    // 客戶端套接字

    char buff[BUFF_SIZE];

    struct sockaddr_in client_addr;

    socklen_t length = sizeof(client_addr);

 

    // 監聽並返回客戶端fd

    int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);

    if (conn < 0) {

        perror("accept");

        exit(3);

    }

 

    while (1) {

        memset(buff, 0, sizeof(buff));

        int len = recv(conn, buff, sizeof(buff), 0);

 

        if (strcmp(buff, "exit\n") == 0)

            break;

 

        printf("server received:%s", buff);

        send(conn, buff, len, 0);

    }

 

    close(conn);

    close(server_sockfd);

    return 0;

}

客戶端代碼:

#include <sys/types.h>

#include <sys/socket.h>

#include <stdio.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <string.h>

#include <stdlib.h>

#include <fcntl.h>

#include <sys/shm.h>

 

 

#define MYPORT 8887

#define BUFF_SIZE 1024

 

int main()

{

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

 

    // 定義server socket

    struct sockaddr_in server_addr;

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

    server_addr.sin_family = AF_INET;

    server_addr.sin_port = htons(MYPORT);

    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");    // 伺服器ip

 

    // 連接伺服器

    if (connect(sock_client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {

        perror("connect");

        exit(1);

    }

 

    char sendbuff[BUFF_SIZE], recvbuff[BUFF_SIZE];

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

        send(sock_client_fd, sendbuff, strlen(sendbuff), 0);        // 發送

        if (strcmp(sendbuff, "exit\n") == 0) break;

 

        recv(sock_client_fd, recvbuff, sizeof(recvbuff), 0);

        printf("client received:%s", recvbuff);

 

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

        memset(sendbuff, 0, sizeof(sendbuff));

    }

 

    close(sock_client_fd);

    return 0;

}

關注「Linux愛好者」

看更多精選技術文章

↓↓↓

相關焦點

  • Linux C語言高級編程之Socket網絡編程!
    Socket網絡編程網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個
  • Linux C網絡編程[Socket]
    現在的跨平臺網絡編程框架很多,如Java的SSH,C/C++的Boost等。  現在的分布式框架很多,如Hadoop等。  我的任務是把一個C/C++程序做成分布式,要求的不配環境,基本屬於純計算的,結果很小。所以選擇了Socket。
  • Linux C Socket Api詳解
    文章主要梳理於《UNIX 環境高級編程第二版》 第十六章 網絡IPC: 套接字以前都只是在網上搜的能用的例子,對一些參數不是很清楚,這次匯總。而且網絡通信還是很常用的通信手段。UNIX 環境高級編程對Socket通信的描述是套接字網絡IPC(進程間通信),可以用於計算機間通信也可用於計算機內通信,管道、消息隊列、信號量以及共享內存等都是屬於計算機內通信的情況。一、 套接字Api詳細介紹1. 套接字描述符首先會先到的是文件描述符,對Linux一切皆文件的哲學又多懂了一點兒點兒。套接字是通信端點的抽象。
  • Python網絡編程socket模塊實例解析
    socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)。(1)利用socket進行簡單的連結Python裡面的socket支持UDP、TCP、以及進程間的通信,socket可以把我們想要發送的東西封裝起來,發送過去,然後反解成原來的樣子,事實上網路通信可以理解成都是建立在socket之上,下面的代碼是演示利用socket進行簡單的連結#要成一次通信,至少要有兩個人,也就是一個服務端
  • Linux系統下socket編程socket接口介紹(二)
    函數介紹- 發送和接收 -(1)send和write:首先說明的一點,之前介紹的socket這個函數,非常類似我們之前介紹的open函數,他們都會返回一下文件描述符;所以這裡的send函數和write函數作用類似,我們用man手冊來查看它的具體形式和用法:   #include <sys/types.h>   #include <sys/socket.h>   ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • Java socket編程
    而TCP層則提供面向應用的可靠(tcp)的或非可靠(UDP)的數據傳輸機制,這是網絡編程的主要對象,一般不需要關心IP層是如何處理數據的。目前較為流行的網絡編程模型是客戶機/伺服器(C/S)結構。即通信雙方一方作為伺服器等待客戶提出請求並予以響應。客戶則在需要服務時向伺服器提 出申請。
  • 讀懂Java中的Socket編程
    TCP和UDP通信  關於socket編程我們有兩種通信協議可以進行選擇。一種是數據報通信,另一種就是流通信。  數據報通信  數據報通信協議,就是我們常說的UDP(User Data Protocol 用戶數據報協議)。UDP是一種無連接的協議,這就意味著我們每次發送數據報時,需要同時發送本機的socket描述符和接收端的socket描述符。
  • Python Socket 網絡編程
    TCP 是一種面向連接的傳輸層協議,TCP Socket 是基於一種 Client-Server 的編程模型,服務端監聽客戶端的連接請求
  • Linux C Socket編程,這篇文章讓我耳目一新
    現在的跨平臺網絡編程框架很多,如Java的SSH,C/C++的Boost等。現在的分布式框架很多,如Hadoop等。我的任務是把一個C/C++程序做成分布式,要求的不配環境,基本屬於純計算的,結果很小。所以選擇了Socket。
  • PHP SOCKET編程
    造成socket_read(): unable to read from socket3.PHP的並發IO編程原文:http://rango.swoole.com/archives/5081) 多進程/多線程同步阻塞最早的伺服器端程序都是通過多進程、多線程來解決並發IO的問題。進程模型出現的最早,從Unix系統誕生就開始有了進程的概念。
  • python socket編程
    else:        if (proto == "http"):            port = 80        elif (proto == "https"):            port = 443    host = dest[0]        if (proto == "http"):        sock = socket.socket
  • Python入門基礎之socket多線程編程,TCP伺服器和客戶端通信
    在上一篇文章中,我介紹了一下python3 socket編程的基礎,包括TCP、UDP客戶端和伺服器的建立過程(連結在最下方)。不過那個只是單線程的,伺服器一次只能和一個客戶端會話,多個客戶端的話只能等待。我們平時的應用中,伺服器肯定是要並發的,所以,今天將介紹socket編程的多線程編程。
  • 基於Socket的java通信編程詳解
    Java最初是作為網絡程式語言出現的,其對網絡提供了高度的支持,使得客戶端和伺服器的溝通變成了現實,而在網絡編程中,使用最多的就是Socket。像大家熟悉的QQ、MSN都使用了Socket相關的技術。下面就讓我們一起揭開Socket的神秘面紗。
  • 簡單說說Python Socket編程步驟?
    廢話不多說,開始今天的題目:問:簡單說說Python socket編程步驟?答:Socket是應用層與TCP/IP協議族通信的中間軟體抽象層,它是一組接口。所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標準的。Python 提供了兩個基本的 socket 模塊。第一個是 Socket,它提供了標準的 BSD Sockets API。第二個是 SocketServer, 它提供了伺服器中心類,可以簡化網絡伺服器的開發。
  • PHP編寫基本的Socket程序
    我的目的因為在以後的學習中,我可能會用到網絡方面的內容,但同時很多寫 PHP 的 coder 都沒寫過 socket 程序,但是肯定聽說過它,也肯定聽說過網絡編程這個詞;所以為了今後的學習,我打算在這裡先簡單的講解下相關知識,本篇博文自帶實例程序,代碼託管在碼云:php-socket-base-code
  • 網絡編程基礎之Socket概述
    常見的Socket類型有以下幾種:流式Socket(SOCK_STREAM):流式socket提供可靠的,面向連接的通信流,它使用TCP協議,從而保證數據傳輸的正確性。數據報(SOCK_DGRAM):數據報Socket定義了一種無連接的服務,它使用UDP協議,通過相互獨立的數據報傳輸數據,協議本身不保證數據傳輸的可靠性及數據的原始順序。
  • Python Socket 編程學習筆記
    Python中的Socket模塊2.1 Socket類型socket.AF_INET : 基於IPV4方式的網絡通信socket.AF_INET : 基於IPV6方式的網絡通信socket.SOCK_STREAM :基於TCP的流式socket通信socket.SOCK_DGRAM
  • Linux伺服器編程簡介
    Linux伺服器編程的特點是異步高並發,代碼不能阻塞、不能休眠,以提高伺服器的並發效率。給nginx寫自定義的模塊,就是典型的Linux伺服器編程。nginx-rtmp-module就是一個開源的nginx模塊,它為nginx添加了rtmp協議的支持。
  • 程式設計師的術與道:術——編程基本功之網絡編程
    編程數據包抓獲和協議分析libpap 等函數庫實現某個協議參考相關的 RFC 文檔,並通過 socket 編程來實現3.1 linux網絡編程步驟3.1.1 創建套接字通過 ip地址 可以確定目標主機,通過埠號可以將數據準確地交給目標程序,而 ip地址:埠號 就是我們所說的 套接字。
  • 用Socket編程之TCP/IP通信,你會了嗎?
    1、socket即為套接字,在TCP/IP協議中,「IP位址+TCP或UDP埠號」唯一的標識網絡通訊中的一個進程,「IP位址+TCP或UDP埠號」就為socket。 2、在TCP協議中,建立連接的兩個進程(客戶端和伺服器)各自有一個socket來標識,則這兩個socket組成的socket pair就唯一標識一個連接。