国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))

這篇具有很好參考價值的文章主要介紹了【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

橙色

1、socket套接字介紹

所謂套接字,就是對網(wǎng)絡(luò)中不同主機(jī)上的應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象。

一個套接字就是網(wǎng)絡(luò)上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)絡(luò)協(xié)議交換數(shù)據(jù)的機(jī)制。從所處的地位來講,套接字上聯(lián)應(yīng)用進(jìn)程,下聯(lián)網(wǎng)絡(luò)協(xié)議棧,是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議進(jìn)程通信的接口,是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議進(jìn)行交互的接口。

它是網(wǎng)絡(luò)環(huán)境中進(jìn)行通信的API,使用中每一個套接字都有一個與之相連進(jìn)程。通信時其中一個網(wǎng)絡(luò)應(yīng)用程序?qū)⒁獋鬏數(shù)囊欢涡畔懭胨诘闹鳈C(jī)socket中,該socket通過與網(wǎng)絡(luò)接口卡(NIC)相連的傳輸介質(zhì)將這段信息送到另一臺主機(jī)的socket中,使對方能夠接收到這段信息。socket是由IP地址和端口結(jié)合的,提供應(yīng)用層進(jìn)程傳送數(shù)據(jù)包的機(jī)制。

socket本意上“插座”的意思,在Linux環(huán)境中,用于表示進(jìn)程間網(wǎng)絡(luò)通信的特殊文件類型。本質(zhì)上為內(nèi)核借助緩沖區(qū)形成的偽文件。把它設(shè)置為文件,方便我們進(jìn)行操作,我們可以通過文件描述符進(jìn)行操作。與管道類型,Linux系統(tǒng)將期封裝成文件的目的是為了統(tǒng)一接口,使得讀寫套接字和讀寫文件操作一樣。區(qū)別是管道應(yīng)用于本地進(jìn)程間通信,而套接字多用于網(wǎng)絡(luò)進(jìn)程間數(shù)據(jù)的傳遞。

socket是全雙工通信,即在同一時刻既可以數(shù)據(jù)讀入,也可以數(shù)據(jù)輸出。
【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))

IP地址(邏輯地址): 在網(wǎng)絡(luò)中唯一標(biāo)識一臺主機(jī)
端口號:在一臺主機(jī)中唯一標(biāo)識一個進(jìn)程
IP+端口號:在網(wǎng)絡(luò)環(huán)境中唯一標(biāo)識一個進(jìn)程

-服務(wù)器端:被動接受連接,一般不會主動發(fā)起連接

-客戶端:主動向服務(wù)器發(fā)起連接

2、字節(jié)序

簡介

現(xiàn)在CPU的累加器一次都能裝載(至少)4個字節(jié)(32位機(jī)),即一個整數(shù)。那么這4個字節(jié)在內(nèi)存中排列的順序?qū)⒂绊懰焕奂悠餮b載的整數(shù)值,這就是字節(jié)序問題。在各種計算機(jī)體系結(jié)構(gòu)中,對于字節(jié)、字等的存儲機(jī)制有所不同,因而引發(fā)了計算機(jī)通信領(lǐng)域中一個很重要的問題,即通信雙方交流的信息單元應(yīng)該以什么樣的順序進(jìn)行傳送。如果不達(dá)成一致的規(guī)則,通信雙方將無法進(jìn)行正確的編碼/譯碼從而導(dǎo)致通信失敗。

字節(jié)序,顧名思義寧節(jié)的順序,就是大于一個寧節(jié)類型的數(shù)據(jù)在內(nèi)存中的存放順序(一個字節(jié)的數(shù)據(jù)當(dāng)然就無需談順序的問題了)。

字節(jié)序分為大端字節(jié)序(Big-Endian)和小端字節(jié)序(Little-Endian)。大端字節(jié)序是指一個整數(shù)的高位字節(jié)存儲在內(nèi)存的低地址位置,低位字節(jié)存儲在內(nèi)存的高地址位置。小端字節(jié)序則是指一個整數(shù)的高位字節(jié)存儲在內(nèi)存高地址處,而低位字節(jié)則存儲在內(nèi)存的低地址處。

【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))

顯然,一個數(shù),越靠左邊的是高位,越靠右邊的是低位

下面,編寫一個程序檢測當(dāng)前主機(jī)的字節(jié)序:
對聯(lián)合體不了解的可以參考這篇文章——C語言 | 聯(lián)合體詳解

/*  
    字節(jié)序:字節(jié)在內(nèi)存中存儲的順序。
    小端字節(jié)序:數(shù)據(jù)的高位字節(jié)存儲在內(nèi)存的高位地址,低位字節(jié)存儲在內(nèi)存的低位地址
    大端字節(jié)序:數(shù)據(jù)的低位字節(jié)存儲在內(nèi)存的高位地址,高位字節(jié)存儲在內(nèi)存的低位地址
*/

// 通過代碼檢測當(dāng)前主機(jī)的字節(jié)序
#include <stdio.h>

int main() {

    union {
        short value;    // 2字節(jié)
        char bytes[sizeof(short)];  // char[2]
    } test; 

    test.value = 0x0102;
    if((test.bytes[0] == 1) && (test.bytes[1] == 2)) {
        printf("大端字節(jié)序\n");
    } else if((test.bytes[0] == 2) && (test.bytes[1] == 1)) {
        printf("小端字節(jié)序\n");
    } else {
        printf("未知\n");
    }

    return 0;
}

【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))

字節(jié)序轉(zhuǎn)換函數(shù)

當(dāng)格式化的數(shù)據(jù)在兩臺使用不同字節(jié)序的主機(jī)之間直接傳遞時,接收端必然會錯誤的解釋。解決問題的方法是:發(fā)送端總是把要發(fā)送的數(shù)據(jù)轉(zhuǎn)換成大端字節(jié)序數(shù)據(jù)后再發(fā)送,而接收端知道對方傳送過來的數(shù)據(jù)總是采用大端字節(jié)序,所以接收端可以根據(jù)自身采用的字節(jié)序決定是否對接收到的數(shù)據(jù)進(jìn)行轉(zhuǎn)換(小端機(jī)轉(zhuǎn)換,大端機(jī)不轉(zhuǎn)換)。

網(wǎng)絡(luò)字節(jié)順序是TCPIP中規(guī)定好的一種數(shù)據(jù)表示格式,它與具體的CPU類型、操作系統(tǒng)等無關(guān),從而可以保證數(shù)據(jù)在不同主機(jī)之間傳輸時能夠被正確解釋,網(wǎng)絡(luò)字節(jié)順序采用大端排序方式。

BSD Socke t提供了封裝好的轉(zhuǎn)換接口,方便程序員使用。包括從主機(jī)字節(jié)序到網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換函數(shù): htons,htonl;從網(wǎng)絡(luò)字節(jié)序到主機(jī)字節(jié)序的轉(zhuǎn)換函數(shù): ntohs、ntohl。

/*
h - host   主機(jī),主機(jī)字節(jié)序

to   轉(zhuǎn)換成什么

n - network   網(wǎng)絡(luò)字節(jié)序

s - short unsigned short   端口

l - long unsigned int   IP

 網(wǎng)絡(luò)通信時,需要將主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序(大端),
    另外一段獲取到數(shù)據(jù)以后根據(jù)情況將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序。

    // 轉(zhuǎn)換端口
    uint16_t htons(uint16_t hostshort);		// 主機(jī)字節(jié)序 - 網(wǎng)絡(luò)字節(jié)序
    uint16_t ntohs(uint16_t netshort);		// 網(wǎng)絡(luò)字節(jié)序 - 主機(jī)字節(jié)序

    // 轉(zhuǎn)IP
    uint32_t htonl(uint32_t hostlong);		// 主機(jī)字節(jié)序 - 網(wǎng)絡(luò)字節(jié)序
    uint32_t ntohl(uint32_t netlong);		// 網(wǎng)絡(luò)字節(jié)序 - 主機(jī)字節(jié)序
*/

#include <stdio.h>
#include <arpa/inet.h>

int main() {

    // htons 轉(zhuǎn)換端口
    unsigned short a = 0x0102;
    printf("a : %x\n", a);
    unsigned short b = htons(a);
    printf("b : %x\n", b);

    printf("=======================\n");

    // htonl  轉(zhuǎn)換IP
    char buf[4] = {192, 168, 1, 100};
    int num = *(int *)buf;
    printf("num : %d\n", num);
    
    int sum = htonl(num);
    unsigned char *p = (char *)&sum;

    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));

    printf("=======================\n");

    // ntohl
    unsigned char buf1[4] = {1, 1, 168, 192};
    int num1 = *(int *)buf1;
    int sum1 = ntohl(num1);
    unsigned char *p1 = (unsigned char *)&sum1;
    printf("%d %d %d %d\n", *p1, *(p1+1), *(p1+2), *(p1+3));
    
     // ntohs


    return 0;
}

【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))

問題:打印num是1677830336是怎么回事呢?
答: // 192 ???? 168 ???????1 ????????100
? // 11000000 10101000 000000001 01101000
//本機(jī)是小端字節(jié)序,所以在192在低位,100在高位,所以num為
// 01101000 ?00000001 ?10101000 ?11000000 = 1677830336

3、socket地址

socket網(wǎng)絡(luò)編程接口中表示socket地址是結(jié)構(gòu)體sockaddr,其定義如下:

#include <bits/socket.h>

struct sockaddr{                                //已經(jīng)被廢棄掉

        sa_family_t sa_family;
        char sa_data[14];
};

typedef unsigned short int sa_family_t;

成員:
????sa_family成員是地址族類型(sa_family_t)的變量。地址族類型通常與協(xié)議類型對應(yīng)。常見的協(xié)議族和對應(yīng)的地址族如下所示:

協(xié)議族 地址族 描述
PF_UNIX AF_UNIX UNIX本地域協(xié)議族
PF_INET AF_INET TCP/IPv4協(xié)議族
PF_INET6 AF_INET6 TCP/IPv6協(xié)議族

協(xié)議族 PF_*和地址族AF_*都定義在頭文件bits/socket.h中,二者值相同,可以混合使用(反正都是宏定義,宏定義是預(yù)處理階段進(jìn)行宏替換,所以混著用對編譯運(yùn)行不會有影響)

而sa_data成員用于存放socket地址值。但是,不同的協(xié)議族的地址值具有不同的含義和長度
【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))
可以看到,14個字節(jié)只能裝下 IPv4地址,沒辦法裝下IPv6的地址。因此,該結(jié)構(gòu)體表示方式已經(jīng)被廢掉,Linux定義了下面這個新的通用的socket地址結(jié)構(gòu)體,這個結(jié)構(gòu)體不僅提供了足夠大的空間用于存放地址值,而且是內(nèi)存對齊的【內(nèi)存對齊可以加快CPU訪問速度】

這個結(jié)構(gòu)體定義在:/usr/include/linux/in.h

#include <bits/socket.h>
struct sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_align; //不用管,用來作內(nèi)存對齊的
char __ss_padding[ 128 - sizeof(__ss_align) ];
};
typedef unsigned short int sa_family_t;

專用socket地址

很多網(wǎng)絡(luò)編程函數(shù)誕生早于IPv4協(xié)議(用自定義的協(xié)議咯,雙方共同約定一個規(guī)則),那時候都是使用struck socketaddr結(jié)構(gòu)體,*為了向前兼容,現(xiàn)在在socketaddr退化成了 (void )的作用,傳遞一個地址給函數(shù),至于這個函數(shù)是sockaddr_in還是sockaddr_in6,由地址族確定,然后函數(shù)內(nèi)部再強(qiáng)制類型轉(zhuǎn)化為所需的地址類型。

主要要記住的就是下圖中的第二個struct sockaddr_in
【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))
UNIX 本地域協(xié)議族使用如下專用的 socket 地址結(jié)構(gòu)體:

#include <sys/un.h>
struct sockaddr_un
{
sa_family_t sin_family;
char sun_path[108];
};

TCP/IP 協(xié)議族有 sockaddr_in 和 sockaddr_in6 兩個專用的 socket 地址結(jié)構(gòu)體,它們分別用于 IPv4 和 IPv6:

#include <netinet/in.h>
struct sockaddr_in
{
sa_family_t sin_family;         /* __SOCKADDR_COMMON(sin_) */
in_port_t sin_port;             /* Port number. 2個字節(jié)的端口號 */
struct in_addr sin_addr;        /* Internet address. 4個字節(jié)的ip地址 */

/* Pad to size of `struct sockaddr'.  剩余填充的部分*/
unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) - sizeof (struct in_addr)];
};


struct in_addr
{
in_addr_t s_addr;
};


struct sockaddr_in6
{
sa_family_t sin6_family;
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};


typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

所有專用 socket 地址(以及 sockaddr_storage)類型的變量在實際使用時都需要轉(zhuǎn)化為通用 socket 地 址類型 sockaddr(強(qiáng)制轉(zhuǎn)化即可),因為所有 socket 編程接口使用的地址參數(shù)類型都是 sockaddr

4、IP地址轉(zhuǎn)換函數(shù)

人們習(xí)慣用可讀性好的字符串來表示IP地址,比如用點(diǎn)分十進(jìn)制字符串表示IPV4地址,以及用十六進(jìn)制字符串表示IPv6地址,但編程中我們需要先把他們轉(zhuǎn)化為整數(shù)(二進(jìn)制)方能使用。而記錄日志相反,我們需要把整數(shù)表示的IP地址轉(zhuǎn)化為可讀的字符串。

p:點(diǎn)分十進(jìn)制的IP字符串

n:表示network,網(wǎng)絡(luò)字節(jié)序的整數(shù)

#include  <arpa/inet.h>

將IP地址從字符串形式轉(zhuǎn)化為二進(jìn)制整數(shù)形式
int inet_pton(int af,const char *src,void *dst);

af:地址族: AF_INET AF_INET6

src:需要轉(zhuǎn)換的點(diǎn)分十進(jìn)制的IP字符串

dst:轉(zhuǎn)換后的結(jié)果保存在這個里面

將網(wǎng)絡(luò)字節(jié)序的整數(shù),轉(zhuǎn)換成點(diǎn)分十進(jìn)制的IP地址字符串
const char *inet_ntop(int af,const void *src,char *dst,socklen_t size);

af:AF_INET   AF_INE6

src: 要轉(zhuǎn)換的ip的整數(shù)的地址

dst: 轉(zhuǎn)換成的IP地址字符串保存的地方

size:第三個參數(shù)的大?。〝?shù)組的大?。?
返回值:返回轉(zhuǎn)換后的數(shù)據(jù)的地址(字符串),和 dst 是一樣的

點(diǎn)分十進(jìn)制 --->  網(wǎng)絡(luò)字節(jié)序   inet_pton

網(wǎng)絡(luò)字節(jié)序 --->  點(diǎn)分十進(jìn)制   inet_ntop

代碼舉例:

/*
    #include <arpa/inet.h>
    // p:點(diǎn)分十進(jìn)制的IP字符串,n:表示network,網(wǎng)絡(luò)字節(jié)序的整數(shù)
    int inet_pton(int af, const char *src, void *dst);
        af:地址族: AF_INET  AF_INET6
        src:需要轉(zhuǎn)換的點(diǎn)分十進(jìn)制的IP字符串
        dst:轉(zhuǎn)換后的結(jié)果保存在這個里面

    // 將網(wǎng)絡(luò)字節(jié)序的整數(shù),轉(zhuǎn)換成點(diǎn)分十進(jìn)制的IP地址字符串
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
        af:地址族: AF_INET  AF_INET6
        src: 要轉(zhuǎn)換的ip的整數(shù)的地址
        dst: 轉(zhuǎn)換成IP地址字符串保存的地方
        size:第三個參數(shù)的大?。〝?shù)組的大?。?        返回值:返回轉(zhuǎn)換后的數(shù)據(jù)的地址(字符串),和 dst 是一樣的

*/

#include <stdio.h>
#include <arpa/inet.h>


int main() {

    // 創(chuàng)建一個ip字符串,點(diǎn)分十進(jìn)制的IP地址字符串
    char buf[] = "192.168.1.4";
    unsigned int num = 0;

    // 將點(diǎn)分十進(jìn)制的IP字符串轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序的整數(shù)
    inet_pton(AF_INET, buf, &num);
    unsigned char * p = (unsigned char *)&num;
    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));


    // 將網(wǎng)絡(luò)字節(jié)序的IP整數(shù)轉(zhuǎn)換成點(diǎn)分十進(jìn)制的IP字符串
    char ip[16] = ""; //字符串IP地址四段,每段最多三個字節(jié),加上3個“.”,再加一個字符串結(jié)束符
    const char * str =  inet_ntop(AF_INET, &num, ip, 16);
    printf("str : %s\n", str);
    printf("ip : %s\n", ip);
    printf("%d\n", ip == str);

    return 0;
}

【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))

5、套接字函數(shù)

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//包含了這個頭文件,上面兩個就可以省略

int socket(int domain,int type,int protoco1);
	- 功能:創(chuàng)建一個套接字
	- 參數(shù):
		- domain:協(xié)議族
			AF_INET:ipv4
			AF_INET6:ipv6
			AF_UNIX,AF_LOCAL:本地套接字通信(進(jìn)程間通信)
		- type:通信過程中使用的協(xié)議類型
			SOCK_STREAM:流式協(xié)議(TCP等)
			SOCK_DGRAM:報式協(xié)議(UDP等)
		- protocol:具體的一個協(xié)議。一般寫0
			- SOCK_STREAM:流式協(xié)議默認(rèn)使用TCP
			- SOCK_DGRAM:報式協(xié)議默認(rèn)使用UDP
		- 返回值:
			- 成功:返回文件描述符,操作的就是內(nèi)核緩沖區(qū)
			- 失?。?span id="n5n3t3z"    class="token operator">-1	
			
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
	- 功能:綁定,將fd和本地的IP+端口進(jìn)行綁定
	- 參數(shù):
			- socket:通過socket函數(shù)得到的文件描述符
			- addr:需要綁定的socket地址,這個地址封裝了ip和端口號的信息
			- addr len:第二個參數(shù)結(jié)構(gòu)體占的內(nèi)存大小
			- 返回值:成功返回0,失敗返回-1
			
int listen(int sockfd,int backlog);// /proc/sys/net/cor e/somaxconn
	- 功能:監(jiān)聽這個socket上的連接
	- 參數(shù):
		- sockfd:通過socket()函數(shù)得到的文件描述符
		- backlog:未連接的和已連接的和的最大值,超過該設(shè)定的最大值的連接會被舍棄掉。但該設(shè)定值不能超過/proc/sys/net/cor e/somaxconn這個文件里的數(shù)值
		
int accept(int sockfd,struct sockaddr *addr ,sock1en_t *addrlen);
	- 功能:接收客戶端連接,默認(rèn)是一個阻塞的函數(shù),阻塞等待客戶的連接
	- 參數(shù):
			- sockfd:用于監(jiān)聽的文件描述符
			- addr:傳出參數(shù),記錄了連接成功后客戶端的地址信息(IP和端口號)
			- addrlen:指定第二個參數(shù)的對應(yīng)的內(nèi)存的大小
	- 返回值:
			- 成功:返回用于通信的文件描述符
			- -1:失敗
			
int connect(int sockfd,const struct sockaddr *addr,socklen_t addr1en);
	- 功能:客戶端連接服務(wù)器
	- 參數(shù):
			- sockfd:用于通信的文件描述符 
			- addr:客戶端要連接的服務(wù)器的地址信息
			- addrlen:第二個參數(shù)的內(nèi)存大小
	- 返回值:成功返回0,時報返回-1

ssize_t write(int fd,const void *buf, size_t count);
ssize_t read(int fd,void *buf, size_t count);

6、TCP通信實現(xiàn)(服務(wù)器端和客戶端)

注意,該程序是根據(jù)TCP通信過程中服務(wù)器的接收信息的步驟寫的,可以先參考該篇文章。

服務(wù)器端

// TCP 通信的服務(wù)器端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.創(chuàng)建socket(用于監(jiān)聽的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.綁定
    struct sockaddr_in saddr;        //這個結(jié)構(gòu)體本文章的上半部分有詳細(xì)的介紹,不了解可以去看看
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "192.168.193.128", &saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.監(jiān)聽
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.接收客戶端連接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    // 輸出客戶端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);

    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        
        // 獲取客戶端的數(shù)據(jù)
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客戶端斷開連接
            printf("clinet closed...");
            break;
        }

        char * data = "hello,i am server";
        // 給客戶端發(fā)送數(shù)據(jù)
        write(cfd, data, strlen(data));
    }
   
    // 關(guān)閉文件描述符
    close(cfd);
    close(lfd);

    return 0;
}

問題:假設(shè)服務(wù)端先調(diào)用一次read把客戶端文件描述符中的內(nèi)容讀完了,這時候客戶端沒有往描述符中寫數(shù)據(jù)了,但是也沒有斷開連接,那此時服務(wù)器第二次調(diào)用read會返回什么?
答:讀管道的特點(diǎn),當(dāng)管道中無數(shù)據(jù):1、寫端被全部關(guān)閉,read返回0(相當(dāng)于讀到文件的末尾)2、寫端沒有完全關(guān)閉,read阻塞等待。參考我的這篇文章【Linux】管道的讀寫特點(diǎn)和管道設(shè)置為非阻塞

客戶端

// TCP通信的客戶端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.創(chuàng)建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.連接服務(wù)器端,注意是要服務(wù)器的ip地址和端口
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.177.146", &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    if(ret == -1) {
        perror("connect");
        exit(-1);
    }

    
    // 3. 通信
    char recvBuf[1024] = {0};
    while(1) {

        char * data = "hello,i am client";
        // 給客戶端發(fā)送數(shù)據(jù)
        write(fd, data , strlen(data));

        sleep(1);
        
        int len = read(fd, recvBuf, sizeof(recvBuf));
        if(len == -1) {
            perror("read");
            exit(-1);
        } else if(len > 0) {
            printf("recv server data : %s\n", recvBuf);
        } else if(len == 0) {
            // 表示服務(wù)器端斷開連接
            printf("server closed...");
            break;
        }

    }

    // 關(guān)閉連接
    close(fd);

    return 0;
}

客戶端第21行中的ip地址應(yīng)該寫自己主機(jī)的ip地址,我的主機(jī)的ip地址為192.168.177.146

分別將兩個文件編譯并執(zhí)行。得到結(jié)果如下:
【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))
【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))
可以看到,在服務(wù)器的執(zhí)行程序中,打印出了客戶端的ip為192.168.177.146,端口隨機(jī)分配為35302

注意:要先啟動服務(wù)端,再啟動客戶端。

作業(yè):把服務(wù)器改為回頻服務(wù)器,也就是服務(wù)器把客戶端發(fā)送來的數(shù)據(jù)再發(fā)送回去。把客戶端改為從鍵盤輸入數(shù)據(jù)并發(fā)送給服務(wù)端。所以最后的效果就是,我從鍵盤輸入,客戶端發(fā)送給服務(wù)端,服務(wù)端再把相同的內(nèi)容發(fā)送回來。

服務(wù)端:

// TCP 通信的服務(wù)器端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.創(chuàng)建socket(用于監(jiān)聽的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.綁定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "192.168.193.128", &saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    if(ret == -1) {
        perror("bind");
        exit(-1);
    }

    // 3.監(jiān)聽
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }

    // 4.接收客戶端連接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }

    // 輸出客戶端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);

    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        memset(recvBuf, 0, 1024);
        // 獲取客戶端的數(shù)據(jù)
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客戶端斷開連接
            printf("clinet closed...");
            break;
        }

        char * data = recvBuf;;
        // 給客戶端發(fā)送數(shù)據(jù)
        write(cfd, data, strlen(data));
    }
   
    // 關(guān)閉文件描述符
    close(cfd);
    close(lfd);

    return 0;
}

客戶端:

// TCP通信的客戶端

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    // 1.創(chuàng)建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }

    // 2.連接服務(wù)器端,注意是要服務(wù)器的ip地址和端口
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.177.146", &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    if(ret == -1) {
        perror("connect");
        exit(-1);
    }

    
    // 3. 通信
    char recvBuf[1024] = {0};
    while(1) {

        char data[1024];
        memset(data, 0, 1024);
        printf("請輸入發(fā)送數(shù)據(jù):\n");
        scanf("%s", data);
        // 給客戶端發(fā)送數(shù)據(jù)
        write(fd, data , strlen(data));

        sleep(1);
        
        memset(recvBuf, 0, 1024);
        int len = read(fd, recvBuf, sizeof(recvBuf));
        if(len == -1) {
            perror("read");
            exit(-1);
        } else if(len > 0) {
            printf("recv server data : %s\n", recvBuf);
        } else if(len == 0) {
            // 表示服務(wù)器端斷開連接
            printf("server closed...");
            break;
        }

    }

    // 關(guān)閉連接
    close(fd);

    return 0;
}

【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))
【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))文章來源地址http://www.zghlxwxcb.cn/news/detail-492778.html

到了這里,關(guān)于【Linux】socket 編程(socket套接字介紹、字節(jié)序、socket地址、IP地址轉(zhuǎn)換函數(shù)、套接字函數(shù)、TCP通信實現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • socket | 網(wǎng)絡(luò)套接字、網(wǎng)絡(luò)字節(jié)序、sockaddr結(jié)構(gòu)

    socket | 網(wǎng)絡(luò)套接字、網(wǎng)絡(luò)字節(jié)序、sockaddr結(jié)構(gòu)

    歡迎關(guān)注博主 Mindtechnist 或加入【Linux C/C++/Python社區(qū)】一起學(xué)習(xí)和分享Linux、C、C++、Python、Matlab,機(jī)器人運(yùn)動控制、多機(jī)器人協(xié)作,智能優(yōu)化算法,濾波估計、多傳感器信息融合,機(jī)器學(xué)習(xí),人工智能等相關(guān)領(lǐng)域的知識和技術(shù)。 專欄:《網(wǎng)絡(luò)編程》 Socket本身有“插座”的意思

    2024年02月05日
    瀏覽(16)
  • Linux網(wǎng)絡(luò)編程:Socket套接字編程(Server服務(wù)器 Client客戶端)

    Linux網(wǎng)絡(luò)編程:Socket套接字編程(Server服務(wù)器 Client客戶端)

    文章目錄: 一:定義和流程分析 1.定義 2.流程分析? 3.網(wǎng)絡(luò)字節(jié)序 二:相關(guān)函數(shù)? IP地址轉(zhuǎn)換函數(shù)inet_pton inet_ntop(本地字節(jié)序 網(wǎng)絡(luò)字節(jié)序) socket函數(shù)(創(chuàng)建一個套接字) bind函數(shù)(給socket綁定一個服務(wù)器地址結(jié)構(gòu)(IP+port)) listen函數(shù)(設(shè)置最大連接數(shù)或者說能同時進(jìn)行三次握手的最

    2024年02月12日
    瀏覽(35)
  • 【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字 -- 基于socket實現(xiàn)一個簡單UDP網(wǎng)絡(luò)程序

    【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字 -- 基于socket實現(xiàn)一個簡單UDP網(wǎng)絡(luò)程序

    我們把數(shù)據(jù)從A主機(jī)發(fā)送到B主機(jī),是目的嗎?不是,真正通信的不是這兩個機(jī)器!其實是這兩臺機(jī)器上面的軟件(人) 數(shù)據(jù)有 IP(公網(wǎng)) 標(biāo)識一臺唯一的主機(jī) ,用誰來標(biāo)識各自主機(jī)上客戶或者服務(wù)進(jìn)程的唯一性呢? 為了更好的表示一臺主機(jī)上服務(wù)進(jìn)程的唯一性,我們采用 端口號

    2024年02月12日
    瀏覽(848)
  • Linux下基于TCP協(xié)議的Socket套接字編程(客戶端&服務(wù)端)入門詳解

    Linux下基于TCP協(xié)議的Socket套接字編程(客戶端&服務(wù)端)入門詳解

    寫在前面: 本篇博客探討實踐環(huán)境如下: 1.操作系統(tǒng): Linux 2.版本(可以通過命令 cat /etc/os-release 查看版本信息):PRETTY_NAME=“CentOS Linux 7 (Core)” 編程語言:C 常常說socket 、套接字 那么socket 到底指的是什么? socket 本質(zhì)上是一個抽象的概念,它是一組用于 網(wǎng)絡(luò)通信的 API , 提供

    2024年02月01日
    瀏覽(19)
  • 【Python】Python 網(wǎng)絡(luò)編程 ( Socket 套接字簡介 | Socket 套接字使用步驟 | Socket 套接字服務(wù)端與客戶端開發(fā) )

    【Python】Python 網(wǎng)絡(luò)編程 ( Socket 套接字簡介 | Socket 套接字使用步驟 | Socket 套接字服務(wù)端與客戶端開發(fā) )

    Socket 套接字 是一種 進(jìn)程之間的 通信機(jī)制 , 通過套接字可以在 不同的進(jìn)程之間 進(jìn)行數(shù)據(jù)交換 ; 在 網(wǎng)絡(luò)編程 中 , Socket 套接字 主要用于 客戶端 與 服務(wù)器 之間的 通信 , 大部分 網(wǎng)絡(luò)相關(guān)的應(yīng)用程序 , 都使用到了 Socket 套接字技術(shù) ; 套接字有兩種類型 : 流套接字 : 提供了一個可

    2024年02月15日
    瀏覽(604)
  • 「網(wǎng)絡(luò)編程」第二講:socket套接字(四 - 完結(jié))_ Linux任務(wù)管理與守護(hù)進(jìn)程 | TCP協(xié)議通訊流程

    「網(wǎng)絡(luò)編程」第二講:socket套接字(四 - 完結(jié))_ Linux任務(wù)管理與守護(hù)進(jìn)程 | TCP協(xié)議通訊流程

    「前言」文章是關(guān)于網(wǎng)絡(luò)編程的socket套接字方面的,上一篇是網(wǎng)絡(luò)編程socket套接字(三),這篇續(xù)上篇文章的內(nèi)容,下面開始講解!? 「歸屬專欄」網(wǎng)絡(luò)編程 「主頁鏈接」個人主頁 「筆者」楓葉先生(fy) 「楓葉先生有點(diǎn)文青病」「句子分享」 Time?goes?on?and?on,?never?to?an?

    2024年02月10日
    瀏覽(47)
  • 網(wǎng)絡(luò)編程套接字(Socket)

    網(wǎng)絡(luò)編程套接字(Socket)

    認(rèn)識IP地址, 端口號, 網(wǎng)絡(luò)字節(jié)序等網(wǎng)絡(luò)編程中的基本概念; 學(xué)習(xí)socket api的基本用法; 能夠?qū)崿F(xiàn)一個簡單的udp客戶端/服務(wù)器; 能夠?qū)崿F(xiàn)一個簡單的tcp客戶端/服務(wù)器(單連接版本, 多進(jìn)程版本, 多線程版本); 理解tcp服務(wù)器建立連接, 發(fā)送數(shù)據(jù), 斷開連接的流程; 通俗易懂地說,源

    2024年01月21日
    瀏覽(106)
  • 【網(wǎng)絡(luò)編程】socket套接字

    【網(wǎng)絡(luò)編程】socket套接字

    如果我們的臺式機(jī)或者筆記本沒有IP地址就無法上網(wǎng),而因為每臺主機(jī)都有IP地址,所以注定了數(shù)據(jù)從一臺主機(jī)傳輸?shù)搅硪慌_主機(jī) 一定有源IP和目的IP 。 所以在報頭中就會包含源IP和目的IP。 而我們把數(shù)據(jù)從一臺主機(jī)傳遞到另一臺主機(jī)并不是目的,真正通信的其實是應(yīng)用層上的

    2024年02月02日
    瀏覽(95)
  • 網(wǎng)絡(luò)編程—Socket套接字詳解

    網(wǎng)絡(luò)編程—Socket套接字詳解

    目錄 一、網(wǎng)絡(luò)編程 1.1、為什么需要網(wǎng)絡(luò)編程? 1.2、什么是網(wǎng)絡(luò)編程 1.3、發(fā)送端和接收端 ?編輯1.4、請求和響應(yīng) ?編輯1.5、客戶端和服務(wù)端? 二、Socket套接字? 2.1、概念 2.2、分類? 2.2.1、流套接字? 2.2.2、數(shù)據(jù)報套接字? 2.2.3、原始套接字? 2.3、Socket編程注意事項? 1.1、為什

    2024年02月16日
    瀏覽(101)
  • 14.1 Socket 套接字編程入門

    14.1 Socket 套接字編程入門

    Winsock是Windows操作系統(tǒng)上的套接字API,用于在網(wǎng)絡(luò)上進(jìn)行數(shù)據(jù)通信。套接字通信是一種允許應(yīng)用程序在計算機(jī)網(wǎng)絡(luò)上進(jìn)行實時數(shù)據(jù)交換的技術(shù)。通過使用Windows提供的API,應(yīng)用程序可以創(chuàng)建一個套接字來進(jìn)行數(shù)據(jù)通信。這個套接字可以綁定到一個端口,以允許其他應(yīng)用程序連接

    2024年02月08日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包