套接字概念
Socket本身有“插座”的意思吱涉,在Linux環(huán)境下逛薇,用于表示進(jìn)程間網(wǎng)絡(luò)通信的特殊文件類型捺疼。本質(zhì)為內(nèi)核借助緩沖區(qū)形成的偽文件。
既然是文件永罚,那么理所當(dāng)然的啤呼,我們可以使用文件描述符引用套接字。與管道類似的呢袱,Linux系統(tǒng)將其封裝成文件的目的是為了統(tǒng)一接口官扣,使得讀寫(xiě)套接字和讀寫(xiě)文件的操作一致。區(qū)別是管道主要應(yīng)用于本地進(jìn)程間通信羞福,而套接字多應(yīng)用于網(wǎng)絡(luò)進(jìn)程間數(shù)據(jù)的傳遞惕蹄。
套接字的內(nèi)核實(shí)現(xiàn)較為復(fù)雜,不宜在學(xué)習(xí)初期深入學(xué)習(xí)治专。
在TCP/IP協(xié)議中卖陵,“IP地址+TCP或UDP端口號(hào)”唯一標(biāo)識(shí)網(wǎng)絡(luò)通訊中的一個(gè)進(jìn)程≌欧澹“IP地址+端口號(hào)”就對(duì)應(yīng)一個(gè)socket泪蔫。欲建立連接的兩個(gè)進(jìn)程各自有一個(gè)socket來(lái)標(biāo)識(shí),那么這兩個(gè)socket組成的socket pair就唯一標(biāo)識(shí)一個(gè)連接挟炬。因此可以用Socket來(lái)描述網(wǎng)絡(luò)連接的一對(duì)一關(guān)系鸥滨。
套接字通信原理如下圖所示:
套接字通訊原理示意
在網(wǎng)絡(luò)通信中,套接字一定是成對(duì)出現(xiàn)的谤祖。一端的發(fā)送緩沖區(qū)對(duì)應(yīng)對(duì)端的接收緩沖區(qū)婿滓。我們使用同一個(gè)文件描述符索發(fā)送緩沖區(qū)和接收緩沖區(qū)。
TCP/IP協(xié)議最早在BSD UNIX上實(shí)現(xiàn)粥喜,為TCP/IP協(xié)議設(shè)計(jì)的應(yīng)用層編程接口稱為socket API⊥怪鳎現(xiàn)在的主要內(nèi)容是socket API,主要介紹TCP協(xié)議的函數(shù)接口额湘,最后介紹UDP協(xié)議和UNIX Domain Socket的函數(shù)接口卿吐。
網(wǎng)絡(luò)編程接口
1、什么是計(jì)算機(jī)網(wǎng)絡(luò)锋华?
? 多個(gè)計(jì)算機(jī)進(jìn)行通信--->計(jì)算機(jī)網(wǎng)絡(luò)嗡官。
2、計(jì)算機(jī)通信的復(fù)雜度
? (1)毯焕、傳輸信息的復(fù)雜度(種類衍腥、內(nèi)容);
? (2)纳猫、信息的數(shù)量
? (3)婆咸、傳輸距離(干擾...)
? (4)、信息的安全問(wèn)題
? (5)芜辕、計(jì)算機(jī)體系的完整性和封閉性尚骄。
? 既要保證計(jì)算機(jī)的封閉性,又要達(dá)成計(jì)算機(jī)的通信侵续。
3倔丈、ip地址
? (1)、IP地址是有限的询兴,需要一種方式將IP地址復(fù)用乃沙。
? (2)、IP地址的復(fù)用導(dǎo)致了數(shù)據(jù)傳遞的復(fù)雜性(ATM诗舰,存儲(chǔ)轉(zhuǎn)發(fā)機(jī)制警儒;路由機(jī)制)。
? (3)眶根、IP地址過(guò)于抽象不方便使用蜀铲,于是給出了IP地址的人文化轉(zhuǎn)義:域名。
? (4)属百、域名只能代表一個(gè)IP網(wǎng)絡(luò)地址记劝,于是就只能代表一個(gè)網(wǎng)絡(luò)上的節(jié)點(diǎn)實(shí)體。
? (5)族扰、實(shí)際上訪問(wèn)節(jié)點(diǎn)的時(shí)候厌丑,本質(zhì)上使用的是IP地址定欧,所以就需要將域名轉(zhuǎn)化為IP地址(DNS...)
4、IP地址的分類
? (1)怒竿、IPv4地址是4字節(jié)的砍鸠,中間以 . 劃分;IPv6地址是16字節(jié)的耕驰;
規(guī)定:在IP地址劃分上爷辱,一般不取全0/全1;
? IP一般分為5類;
? A朦肘、B饭弓、C、D媒抠、E弟断,一般常用的IP地址為A類,B類趴生,C類夫嗓;
A類IP:第一個(gè)字節(jié)是以0開(kāi)頭0000 0000--->0111 11110~127
B類IP:第一個(gè)字節(jié)是以10開(kāi)頭1000 0000--->1011 1111128~191
C類IP:第一個(gè)字節(jié)是以110開(kāi)頭1100 0000--->1101 1111192~223
??(2)、子網(wǎng)掩碼:就是將網(wǎng)絡(luò)號(hào)設(shè)置為1冲秽,主機(jī)號(hào)設(shè)置為0(對(duì)每一個(gè)字節(jié)的位進(jìn)行設(shè)置)舍咖;
? 例:C類IP地址,3個(gè)字節(jié)網(wǎng)絡(luò)號(hào)和一個(gè)字節(jié)的主機(jī)號(hào)锉桑;
? 1111 1111 1111 1111 1111 1111 0000 0000
???? 255?????? .???? 255???? .????? 255???? .???? 0
(3)排霉、子網(wǎng)劃分:此時(shí)就存在C類地址的子網(wǎng)掩碼不一定總是255.255.255.0;
??這還的看C類IP下面有沒(méi)有子網(wǎng)劃分,
? 有子網(wǎng)劃分的話民轴,最后一個(gè)字節(jié)攻柠,也就是主機(jī)號(hào)可能為2段(01/10)、4段(00/01/10/11)
例:192.168.3.11xx xxxx? 1111 1111 1111 1111 1111 11111100 0000
? 此時(shí)對(duì)應(yīng)的子網(wǎng)掩碼為:255.255.255.192
? (4)后裸、IOS和TCP/IP
模型分析
? (5)瑰钮、端口號(hào)
? port:唯一標(biāo)識(shí)應(yīng)用程序的編號(hào);
我們之間通過(guò)QQ微驶、微信浪谴、郵箱進(jìn)行收發(fā)數(shù)據(jù)時(shí),沒(méi)有導(dǎo)致數(shù)據(jù)的錯(cuò)亂接收因苹,是怎么做到的呢苟耻?又是怎么一一對(duì)應(yīng)找到的呢?
? :通過(guò)端口號(hào)扶檐,識(shí)別了電腦上的某一應(yīng)用程序凶杖,也就是找對(duì)應(yīng)的編號(hào)。
? 我們?cè)谶M(jìn)行數(shù)據(jù)的發(fā)送時(shí):首先通過(guò)IP尋找物理計(jì)算機(jī)款筑,在根據(jù)port智蝠,尋找對(duì)應(yīng)的應(yīng)用程序腾么。
??(6)、TCP和UDP
UDP屬于TCP/IP體系中的一部分杈湾。
??TCP協(xié)議和UDP協(xié)議都屬于傳輸層協(xié)議哮翘。
? TCP協(xié)議:
? i>、面向連接的傳輸協(xié)議毛秘、可靠的、同步的阻课;
? ii>叫挟、面向連接的網(wǎng)絡(luò)傳輸特點(diǎn):a、需要有一方主動(dòng)的建立連接限煞,另一方接收連接請(qǐng)求抹恳;b、只有建立了連接之后才能夠進(jìn)行數(shù)據(jù)的傳輸署驻;c奋献、當(dāng)數(shù)據(jù)傳輸完畢之后,就需要釋放連接旺上,由連接的兩端來(lái)共同決定連接是否保持瓶蚂。
? UDP協(xié)議:
? a、面向無(wú)連接的:即就是在進(jìn)行數(shù)據(jù)傳輸?shù)臅r(shí)候宣吱,不需要預(yù)先創(chuàng)建一個(gè)連接窃这;
? b、不可靠的:無(wú)法知道發(fā)送的數(shù)據(jù)是否能夠到達(dá)目的征候,也無(wú)法知道什么時(shí)候能夠到達(dá)目的杭攻。
? c、異步的:
? (7)疤坝、TCP的三次握手兆解、四次揮手
TCP------->至少3次握手(最后一次防止誤按,2次的話跑揉,有可能死鎖)锅睛;??? : 打電話模型
? 模型分析
? TCP-------->4次揮手。? 模型:男女朋友分手模型
? 通過(guò)IP历谍,只能保證物理上的連通衣撬,至于收發(fā)數(shù)據(jù)的形式是什么,都不歸它管扮饶。
? 127.0.0.1:本機(jī)回送地址具练,可作為測(cè)試本機(jī)使用,不安裝網(wǎng)卡也是可以ping通的甜无。
5扛点、TCP的編程實(shí)現(xiàn)
基礎(chǔ)的socket編程對(duì)TCP的就是下面的步驟:
(1)哥遮、模型分析
(2)、代碼實(shí)現(xiàn)
utili.h
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 9090
#define SERVER_IP ?"127.0.0.1"
#define LISTEN_QUEUE ? ?5
#define BUF_SIZE ? ?255
服務(wù)器端代碼:
#include"utili.h"http://TCP
int main(void)
{
int sockSer = socket(AF_INET, SOCK_STREAM, 0);
if(sockSer == -1){
perror("socket");
return -1;
}
struct sockaddr_in addrSer, addrCli;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
int yes = 1;
setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //地址陵究、端口的重用
socklen_t len = sizeof(struct sockaddr);
int res = bind(sockSer, (struct sockaddr *)&addrSer, len);
if(res == -1)
{
perror("bind");
close(sockSer);
return -1;
}
res = listen(sockSer, LISTEN_QUEUE);
if(res == -1)
{
perror("listen");
close(sockSer);
return -1;
}
int sockConn;
char sendbuf[BUF_SIZE];
char recvbuf[BUF_SIZE];
while(1)
{
sockConn = accept(sockSer, (struct sockaddr *)&addrCli, &len);
if(sockConn == -1){
continue;
}
else
{
printf("Server Accept Client Connect OK\n");
}
printf("Ser :>");
scanf("%s", sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0)
break;
send(sockConn, sendbuf, strlen(sendbuf)+1, 0);
recv(sockConn, recvbuf, BUF_SIZE, 0);
printf("Cli :>%s\n", recvbuf);
}
close(sockSer);
return 0;}
客戶端代碼:
#include"utili.h"http://TCP
int main(void)
{
int sockCli = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
struct sockaddr_in addrCli;
addrCli.sin_family = AF_INET;
addrCli.sin_port = htons(7070);
addrCli.sin_addr.s_addr = inet_addr("192.168.1.155");
int yes = 1;
setsockopt(sockCli, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //地址眠饮、端口的重用
socklen_t len = sizeof(struct sockaddr);
int res = bind(sockCli, (struct sockaddr *)&addrCli, len);
if(res == -1)
{
perror("bind");
close(sockCli);
return -1;
}
res = connect(sockCli, (struct sockaddr*)&addrSer, len);
if(res == -1)
{
perror("connect");
close(sockCli);
return -1;
}
else
{
printf("Client Connect Server ok\n");
}
char sendbuf[BUF_SIZE];
char recvbuf[BUF_SIZE];
while(1)
{
connect(sockCli, (struct sockaddr*)&addrSer, len);
recv(sockCli, recvbuf, BUF_SIZE, 0);
printf("Ser :>%s\n", recvbuf);
printf("Cli :>");
scanf("%s", sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0)
break;
send(sockCli, sendbuf, strlen(sendbuf)+1, 0);
}
close(sockCli);
return 0;}
(3)、運(yùn)行結(jié)果
6铜邮、UDP的編程實(shí)現(xiàn)
? 基礎(chǔ)的socket編程對(duì)UDP的就是下面的步驟:
(1)仪召、模型分析
(2)、代碼實(shí)現(xiàn)
utili.h
#include
#include
#include
#include
#include
#include
#define SERVER_PORT ?9090
#define SERVER_IP ? ?"127.0.0.1"
#define LISTEN_QUEUE ?5
#define BUFFER_SIZE ? 255
服務(wù)器端代碼:
#include"utili.h"
int main()
{
int sockSer = socket(AF_INET, SOCK_DGRAM, 0);
if(sockSer == -1)
{
perror("socket");
return -1;
}
struct sockaddr_in addrSer, addrCli;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
socklen_t len = sizeof(struct sockaddr);
int res = bind(sockSer, (struct sockaddr*)&addrSer, len);
if(res == -1)
{
perror("bind");
close(sockSer);
return -1;
}
char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
while(1)
{
recvfrom(sockSer, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrCli, &len);
printf("Cli:>%s\n",recvbuf);
printf("Ser:>");
scanf("%s",sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0){
break;
}
sendto(sockSer, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrCli, len);
}
close(sockSer);
return 0;}
客戶端代碼:
#include"utili.h"
int ?main()
{
int sockCli = socket(AF_INET, SOCK_DGRAM, 0);
if(sockCli == -1){
perror("socket");
return -1;
}
struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
socklen_t ?len = sizeof(struct sockaddr);
while(1){
printf("Cli:>");
scanf("%s",sendbuf);
if(strncmp(sendbuf, "quit", 4) == 0){
break;
}
sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, len);
recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &len);
printf("Ser:>%s\n",recvbuf);
}
close(sockCli);
return 0;
}
(3)松蒜、運(yùn)行結(jié)果
服務(wù)器端截圖
客戶端截圖
? 服務(wù)端的套接字總領(lǐng)全局扔茅,不與任何客戶端進(jìn)行通信,為每一個(gè)新的客戶端所分配一個(gè)新的套接字秸苗,進(jìn)行通信召娜。
? LISTEN_QUEUE:等待隊(duì)列的大小(最多等待多少隊(duì)列);
? UDP:必須先知道服務(wù)器在哪惊楼。
喜歡本文的朋友們玖瘸,歡迎長(zhǎng)按下圖關(guān)注訂閱號(hào)編程小兔崽,收看更多精彩內(nèi)容
每天進(jìn)步一點(diǎn)點(diǎn)檀咙,如果有用給小編點(diǎn)個(gè)贊