Linux網(wǎng)絡(luò)編程<三>

微信公眾號(hào)文章鏈接:https://mp.weixin.qq.com/s?__biz=Mzg5MjAxNzEyMg==&mid=2247484339&idx=1&sn=9789bc9e98852ec0f89c53c16bf2bc5e&chksm=cfc5c33ef8b24a2858ef5852dba89c5cf8ce77aaaed86e91b94bca9018ed3ca41b3ae668ab39&token=418739389&lang=zh_CN#rd

前2篇文章,我寫(xiě)了一些網(wǎng)絡(luò)編程的基礎(chǔ)以及一些網(wǎng)絡(luò)編程需要掌握的基礎(chǔ)。

1:Linux網(wǎng)絡(luò)編程<一>

2:Linux網(wǎng)絡(luò)編程<二>

socket編程

TCP協(xié)議的流程圖

收到確認(rèn)消息后才會(huì)繼續(xù)發(fā)送消息,否則繼續(xù)等待。這樣的好處是傳輸?shù)臄?shù)據(jù)是可靠的,此外它是有連接的傳輸,大多數(shù)網(wǎng)絡(luò)傳輸都是用的TCP。

TCP協(xié)議

(1)面向連接的可靠的傳輸控制協(xié)議,連接的建立需要三次握手,連接的 ? ? 釋放需要進(jìn)行四次握手才能保證連接的建立,數(shù)據(jù)的同步傳輸。

(2)面向字節(jié)流,會(huì)把從上層傳輸下來(lái)的數(shù)據(jù)當(dāng)作是無(wú)結(jié)構(gòu)的字節(jié)流。

(3)一對(duì)一的通信。

(4)TCP在IP協(xié)議的基礎(chǔ)之上添加了序號(hào)機(jī)制,確認(rèn)機(jī)制,超時(shí)重傳機(jī)制,數(shù)據(jù)校驗(yàn),從而保證傳輸?shù)目煽啃?,同時(shí)保證不出現(xiàn)丟失或者是亂序。

TCP通信的基本步驟如下:

服務(wù)端:socket---bind---listen---while(1){---accept---recv---send---close---}---close

客戶(hù)端:socket---------connect---send---recv-----------------close

服務(wù)器端:

1. 創(chuàng)建socket

1sock_fd?=?socket(AF_INET,?SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP

2if(-1==?sock_fd)

3{

4fprintf(stderr,"socket?error:%s\n\a",?strerror(errno));

5exit(1);

6}

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

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

函數(shù)功能

創(chuàng)建一個(gè)套接字;

domain:協(xié)議域(族),決定了套接字的地址類(lèi)型,例如AF_INET決定了要用IPv4地址(32位)與端口號(hào)(16位)的組合。常見(jiàn)的協(xié)議族有:AF_INET、AF_INET6、AF_LOCAL(或稱(chēng)AF_UNIX)、AF_ROUTE等;

type:指定套接字類(lèi)型,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW

protocol:指定socket所使用的傳輸協(xié)議編號(hào),通常為0

返回值

若成功,返回一個(gè)套接字描述符,否則返回-1;

Socket就是一種文件描述符,和普通的打開(kāi)文件一樣,需要檢測(cè)其返回結(jié)果。

2. 設(shè)置socket

1memset(&server_addr,0,sizeof(struct?sockaddr_in));//clear

2server_addr.sin_family?=?AF_INET;

3server_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);//INADDR_ANY:This?machine?all?IP

4server_addr.sin_port?=?htons(PORT_NUMBER);

設(shè)置何種協(xié)議族,設(shè)置本機(jī)IP和端口,也就有了唯一性。

3. 綁定socket

1ret?=?bind(sock_fd,?(struct?sockaddr?*)(&server_addr),sizeof(struct?sockaddr));

2if(-1==?ret)

3{

4fprintf(stderr,"bind?error:%s\n\a",?strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

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

函數(shù)功能

把套接字綁定到本地計(jì)算機(jī)的某一個(gè)端口上;

sockfd:待綁定的套接字描述符

addr:一個(gè)struct sockaddr *指針,指定要綁定給sockfd的協(xié)議地址。內(nèi)容結(jié)構(gòu)由前面的協(xié)議族決定。

addrlen:地址的長(zhǎng)度

返回值

若成功,返回0,否則返回-1,錯(cuò)誤信息存在errno中;

4. 開(kāi)始監(jiān)聽(tīng)

1ret?=listen(sock_fd,?BACKLOG);

2if(-1==?ret)

3{

4fprintf(stderr,"listen?error:%s\n\a",?strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

int listen(int sockfd, int backlog);

函數(shù)功能

使服務(wù)器的這個(gè)端口和IP處于監(jiān)聽(tīng)狀態(tài),等待網(wǎng)絡(luò)中某一客戶(hù)機(jī)的連接請(qǐng)求,最大連接數(shù)量為backlog≤128;

sockfd:待監(jiān)聽(tīng)的套接字描述符

backlog:最大可監(jiān)聽(tīng)和連接的客戶(hù)端數(shù)量

返回值

若成功,返回0,否則返回-1;

5. 阻塞,等待連接

1addr_len?=sizeof(struct?sockaddr);

2new_fd?=?accept(sock_fd,?(struct?sockaddr?*)&client_addr,?&addr_len);

3if(-1==?new_fd)

4{

5fprintf(stderr,"accept?error:%s\n\a",?strerror(errno));

6close(sock_fd);

7exit(1);

8}

1

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

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

函數(shù)功能

接受連接請(qǐng)求,建立起與客戶(hù)機(jī)之間的通信連接。服務(wù)器處于監(jiān)聽(tīng)狀態(tài)時(shí),如果某時(shí)刻獲得客戶(hù)機(jī)的連接請(qǐng)求,此時(shí)并不是立即處理這個(gè)請(qǐng)求,而是將這個(gè)請(qǐng)求放在等待隊(duì)列中,當(dāng)系統(tǒng)空閑時(shí)再處理客戶(hù)機(jī)的連接請(qǐng)求;

當(dāng)accept函數(shù)接受一個(gè)連接時(shí),會(huì)返回一個(gè)新的socket標(biāo)識(shí)符,以后的數(shù)據(jù)傳輸和讀取就要通過(guò)這個(gè)新的socket編號(hào)來(lái)處理,原來(lái)參數(shù)中的socket也可以繼續(xù)使用,繼續(xù)監(jiān)聽(tīng)其它客戶(hù)機(jī)的連接請(qǐng)求;

accept連接成功時(shí),參數(shù)addr所指的結(jié)構(gòu)體會(huì)填入所連接機(jī)器的地址數(shù)據(jù);

sockfd:待監(jiān)聽(tīng)的套接字描述符

addr:指向struct sockaddr的指針,用于返回客戶(hù)端的協(xié)議地址

addrlen:協(xié)議地址的長(zhǎng)度

返回值

若成功,返回一個(gè)由內(nèi)核自動(dòng)生成的一個(gè)全新描述字,代表與返回客戶(hù)的TCP連接,否則返回-1,錯(cuò)誤信息存在errno中;

6. 接收數(shù)據(jù)

1recv_len?=recv(new_fd,?recv_buf,999,0);

2if(recv_len?<=0)

3{

4fprintf(stderr,"recv?error:%s\n\a", strerror(errno));

5close(new_fd);

6exit(1);

7}

8else

9{

10recv_buf[recv_len]?='\0';

11printf("Get?msg?from?client%d:?%s\n",?client_num,?recv_buf);

12}

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

int recv(int sockfd, void *buf, size_t len, int flags);

函數(shù)功能

用新的套接字來(lái)接收遠(yuǎn)端主機(jī)傳來(lái)的數(shù)據(jù),并把數(shù)據(jù)存到由參數(shù)buf指向的內(nèi)存空間;

sockfd:sockfd為前面accept的返回值,即new_fd,也就是新的套接字

buf:指明一個(gè)緩沖區(qū)

len:指明緩沖區(qū)的長(zhǎng)度

flags:通常為0

返回值

若成功,返回接收到的字節(jié)數(shù),另一端已關(guān)閉則返回0,否則返回-1,錯(cuò)誤信息存在errno中;

7. 關(guān)閉socket

1close(sock_fd);

2exit(0);

為了應(yīng)對(duì)多個(gè)連接,并保證它們之間相互獨(dú)立,實(shí)際編程中往往還要加入多進(jìn)程fork()。

讓子進(jìn)程接收數(shù)據(jù),父進(jìn)程繼續(xù)監(jiān)聽(tīng)新的連接。

客戶(hù)機(jī)端:

1. 創(chuàng)建socket

1sock_fd?=?socket(AF_INET,?SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP

2if(-1==?sock_fd)

3{

4fprintf(stderr,"socket?error:%s\n\a",?strerror(errno));

5exit(1);

6}

2. 設(shè)置socket

1memset(&server_addr,0,sizeof(struct?sockaddr_in));//clear

2server_addr.sin_family?=?AF_INET;

3server_addr.sin_port?=?htons(PORT_NUMBER);

其中注意的是,這里設(shè)置的socket內(nèi)容是指 希望連接的服務(wù)器IP和端口號(hào)信息,IP地址來(lái)自用戶(hù)的輸入,并轉(zhuǎn)換格式得到。因此,這里的設(shè)置和服務(wù)器的設(shè)置,要保持內(nèi)容上的一致。

1ret?=?inet_aton(argv[1],?&server_addr.sin_addr);

2if(0==?ret)

3{

4fprintf(stderr,"server_ip?error.\n");

5close(sock_fd);

6exit(1);

7}

3. 連接

1ret?=?connect(sock_fd,?(conststruct?sockaddr?*)&server_addr,sizeof(struct?sockaddr));

2if(-1==?ret)

3{

4fprintf(stderr,"connect?error:%s\n\a",?strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);

函數(shù)功能

用來(lái)請(qǐng)求連接遠(yuǎn)程服務(wù)器,將參數(shù)sockfd的socket連至參數(shù)serv_addr所指定的服務(wù)器IP和端口號(hào)上去;

sockfd:客戶(hù)端的socket套接字

serv_addr:一個(gè)struct sockaddr類(lèi)型的結(jié)構(gòu)體指針變量,存儲(chǔ)著遠(yuǎn)程服務(wù)器的IP與端口號(hào)信息

addrlen:結(jié)構(gòu)體變量的長(zhǎng)度

返回值

若成功,返回0,否則返回-1,錯(cuò)誤信息存在errno中;

4. 發(fā)送

1send_buf?=?send(sock_fd,?send_buf,strlen(send_buf),0);

2if(send_buf?<=0)

3{

4fprintf(stderr,"send?error:%s\n\a",?strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要頭文件

#include <sys/types.h>

#include <sys/socket.h>

函數(shù)格式

int send(int sockfd, const void *buf, int len, int flags);

函數(shù)功能

用來(lái)發(fā)送數(shù)據(jù)給指定的遠(yuǎn)端主機(jī);

sockfd:客戶(hù)端的socket套接字

buf:指明一個(gè)緩沖區(qū)

len:指明緩沖區(qū)的長(zhǎng)度

flags:通常為0

返回值

若成功,返回發(fā)送的字節(jié)數(shù),否則返回-1,錯(cuò)誤信息存在errno中

5. 關(guān)閉socket

1close(sock_fd);

2exit(0);

TCP完整例子:

service:

1#include<stdio.h>

2#include<unistd.h>

3#include<sys/types.h>

4#include<sys/socket.h>

5#include<strings.h>

6#include<string.h>

7#include<ctype.h>

8#include<arpa/inet.h>

9

10#defineSERV_PORT?9527

11

12intmain(void)

13

{

14intsfd,?cfd;

15intlen,?i;

16charbuf[BUFSIZ],?clie_IP[BUFSIZ];

17

18structsockaddr_inserv_addr,clie_addr;

19socklen_tclie_addr_len;

20

21/*創(chuàng)建一個(gè)socket?指定IPv4協(xié)議族?TCP協(xié)議*/

22sfd?=?socket(AF_INET,?SOCK_STREAM,0);

23

24/*初始化一個(gè)地址結(jié)構(gòu)?man?7?ip?查看對(duì)應(yīng)信息*/

25bzero(&serv_addr,sizeof(serv_addr));//將整個(gè)結(jié)構(gòu)體清零

26serv_addr.sin_family?=?AF_INET;//選擇協(xié)議族為IPv4

27serv_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);//監(jiān)聽(tīng)本地所有IP地址

28serv_addr.sin_port?=?htons(SERV_PORT);//綁定端口號(hào)????

29

30/*綁定服務(wù)器地址結(jié)構(gòu)*/

31bind(sfd,?(struct?sockaddr?*)&serv_addr,sizeof(serv_addr));

32

33/*設(shè)定鏈接上限,注意此處不阻塞*/

34listen(sfd,64);//同一時(shí)刻允許向服務(wù)器發(fā)起鏈接請(qǐng)求的數(shù)量

35

36printf("wait?for?client?connect?...\n");

37

38/*獲取客戶(hù)端地址結(jié)構(gòu)大小*/

39clie_addr_len?=sizeof(clie_addr_len);

40/*參數(shù)1是sfd;?參2傳出參數(shù),?參3傳入傳入?yún)?shù),?全部是client端的參數(shù)*/

41cfd?=?accept(sfd,?(struct?sockaddr?*)&clie_addr,?&clie_addr_len);/*監(jiān)聽(tīng)客戶(hù)端鏈接,?會(huì)阻塞*/

42

43printf("client?IP:%s\tport:%d\n",

44inet_ntop(AF_INET,?&clie_addr.sin_addr.s_addr,?clie_IP,sizeof(clie_IP)),

45ntohs(clie_addr.sin_port));

46

47while(1)?{

48/*讀取客戶(hù)端發(fā)送數(shù)據(jù)*/

49len?=?read(cfd,?buf,sizeof(buf));

50write(STDOUT_FILENO,?buf,?len);

51

52/*處理客戶(hù)端數(shù)據(jù)*/

53for(i?=0;?i?<?len;?i++)

54buf[i]?=toupper(buf[i]);

55

56/*處理完數(shù)據(jù)回寫(xiě)給客戶(hù)端*/

57write(cfd,?buf,?len);

58}

59

60/*關(guān)閉鏈接*/

61close(sfd);

62close(cfd);

63

64return0;

65}

client:

1#include<stdio.h>

2#include<unistd.h>

3#include<string.h>

4#include<sys/socket.h>

5#include<arpa/inet.h>

6

7#defineSERV_IP"127.0.0.1"

8#defineSERV_PORT?9527

9

10intmain(void)

11

{

12intsfd,?len;

13structsockaddr_inserv_addr;

14charbuf[BUFSIZ];

15

16/*創(chuàng)建一個(gè)socket?指定IPv4?TCP*/

17sfd?=?socket(AF_INET,?SOCK_STREAM,0);

18

19/*初始化一個(gè)地址結(jié)構(gòu):*/

20bzero(&serv_addr,sizeof(serv_addr));//清零

21serv_addr.sin_family?=?AF_INET;//IPv4協(xié)議族

22inet_pton(AF_INET,?SERV_IP,?&serv_addr.sin_addr.s_addr);//指定IP?字符串類(lèi)型轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序?參3:傳出參數(shù)

23serv_addr.sin_port?=?htons(SERV_PORT);//指定端口?本地轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序

24

25/*根據(jù)地址結(jié)構(gòu)鏈接指定服務(wù)器進(jìn)程*/

26connect(sfd,?(struct?sockaddr?*)&serv_addr,sizeof(serv_addr));

27

28while(1)?{

29/*從標(biāo)準(zhǔn)輸入獲取數(shù)據(jù)*/

30fgets(buf,sizeof(buf),stdin);

31/*將數(shù)據(jù)寫(xiě)給服務(wù)器*/

32write(sfd,?buf,strlen(buf));//寫(xiě)個(gè)服務(wù)器

33/*從服務(wù)器讀回轉(zhuǎn)換后數(shù)據(jù)*/

34len?=?read(sfd,?buf,sizeof(buf));

35/*寫(xiě)至標(biāo)準(zhǔn)輸出*/

36write(STDOUT_FILENO,?buf,?len);

37}

38

39/*關(guān)閉鏈接*/

40close(sfd);

41

42return0;

43}

推薦閱讀:

網(wǎng)絡(luò)編程<一>

Linux I/O復(fù)用--epoll

Linux I/O復(fù)用——poll()

Linux I/O復(fù)用—select()

線(xiàn)程池網(wǎng)絡(luò)服務(wù)

多線(xiàn)程網(wǎng)絡(luò)服務(wù)

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

線(xiàn)程高級(jí)操作

Linux多線(xiàn)程編程

線(xiàn)程

進(jìn)程間通信(IPC)

進(jìn)程間通信(一)

進(jìn)程間通信(二)

????覺(jué)得有用,點(diǎn)贊,分享就是對(duì)小編最大的支持

長(zhǎng)按2秒識(shí)別二維碼關(guān)注公眾號(hào)

歡迎把我推薦給你的朋喲

每天進(jìn)步一點(diǎn)點(diǎn),如果有用給點(diǎn)個(gè)贊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 系統(tǒng)與網(wǎng)絡(luò)編程 select函數(shù) select和pselect多用于I/O操作,他們見(jiàn)識(shí)多個(gè)文件描述符的集合,判斷...
    I踏雪尋梅閱讀 644評(píng)論 0 1
  • 下面為Daytime這個(gè)服務(wù)的源代碼例子,同時(shí)兼容IPV6和IPV4的地址,最后部分有更多說(shuō)明。 單播模式下的Se...
    天楚銳齒閱讀 6,037評(píng)論 0 2
  • 網(wǎng)絡(luò)編程基礎(chǔ)網(wǎng)絡(luò)編程,首先了解計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)是有必要的,著重掌握TCP、IP協(xié)議,理解socket的概念,理解...
    zhile_doing閱讀 1,911評(píng)論 0 1
  • 大綱 一.Socket簡(jiǎn)介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
    VD2012閱讀 2,700評(píng)論 0 5
  • 1、基本知識(shí) epoll是在2.6內(nèi)核中提出的,是之前的select和poll的增強(qiáng)版本。相對(duì)于select和po...
    Daniel521閱讀 819評(píng)論 0 1

友情鏈接更多精彩內(nèi)容