1. socket概念
socket(套接字),用于網(wǎng)絡(luò)中不同主機(jī)間進(jìn)程的通信。
socket是一個(gè)偽文件,包含讀緩沖區(qū)、寫(xiě)緩沖區(qū)。
socket必須成對(duì)出現(xiàn)。
socket可以建立主機(jī)進(jìn)程間的通信,但需要協(xié)議(IPV4、IPV6等)、port端口、IP地址。
2. 客戶(hù)端服務(wù)端socket網(wǎng)絡(luò)通信步驟(TCP)
2.1 服務(wù)器端步驟:
????????(1)創(chuàng)建流式socket套接字。
? ? ? ? ? ? ? ? a)此socket套接字一直用于后續(xù)的監(jiān)聽(tīng)連接。
? ? ? ? ? ? ? ? b)socket函數(shù)。
? ? ? ? (2)綁定本機(jī)IP地址和port。
? ? ? ? ? ? ? ? b)bind函數(shù)。
? ? ? ? (3)監(jiān)聽(tīng)。
? ? ? ? ? ? ? ? a)將socket套接字由主動(dòng)變?yōu)楸粍?dòng)。
? ? ? ? ? ? ? ? b)創(chuàng)建未完成連接隊(duì)列、已完成連接隊(duì)列;未完成連接接經(jīng)歷3次握手才變成已完成連接。
? ? ? ? ? ? ? ? c)listen函數(shù)。
? ? ? ? (4)提取。
? ? ? ? ? ? ? ? a)從已完成連接隊(duì)列提取連接,創(chuàng)建一個(gè)新的已連接socket套接字用于和客戶(hù)端通信。
? ? ? ? ? ? ? ? b)accept函數(shù)。
? ? ? ??(5)讀寫(xiě)數(shù)據(jù)。
? ? ? ? (6)關(guān)閉socket。
2.2 客戶(hù)端步驟:
? ? ? ? (1)創(chuàng)建流式socket套接字。
? ? ? ? ? ? ? ? a)socket函數(shù)。
? ? ?? ?(2)連接服務(wù)器。
? ? ? ? ? ? ? ? a)指定服務(wù)器的IP協(xié)議(IPV4或IPV6)、port、IP地址。
? ? ? ? ? ? ? ? b)connect函數(shù)(該函數(shù)包含TCP的三次握手)。
? ? ?? ?(3)讀寫(xiě)數(shù)據(jù)。
? ? ? ? (4)關(guān)閉socket。
3. socket相關(guān)結(jié)構(gòu)體和函數(shù)
3.1 socket相關(guān)結(jié)構(gòu)體
(1)IPV4套接字結(jié)構(gòu)體
#include<netinet/in.h>
struct sockaddr_in {
sa_family_t sin_family; /* 協(xié)議:AF_INET */
in_port_t sin_port; /* 端口 */
struct in_addr sin_addr; /* IP地址 */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* IP地址的網(wǎng)絡(luò)字節(jié)序 */
};
(2)IPV6套接字結(jié)構(gòu)體
#include<netinet/in6.h>
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
(3)通用套接字結(jié)構(gòu)體
為了接口通用,出現(xiàn)通用套接字結(jié)構(gòu)體。
#include<sys/socket.h>
struct sockaddr {
sa_family_t sa_family; /* AF_INET 或 AF_INET6 */
char sa_data[14]; /* address data */
};
3.2 socket相關(guān)函數(shù)
(1)?socke函數(shù):創(chuàng)建套接字
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
/*
功能:
創(chuàng)建套接字
參數(shù):
domain:
AF_INET
AF_INET6
等等
type:
SOCK_STREAM:TCP流式套接字
SOCK_DGRAM:UDP報(bào)式套接字
SOCK_RAW:組包更多
等等
protocol:0,自動(dòng)填充
返回值:
成功:文件描述符
失?。?1
*/
(2)connect函數(shù):客戶(hù)端連接服務(wù)器
#include<sys/socket.h>
int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
/*
功能:
連接服務(wù)器
參數(shù):
sockfd:套接字文件描述符
addr:IPV4套接字結(jié)構(gòu)體地址 強(qiáng)轉(zhuǎn)為通用套接字結(jié)構(gòu)體
包含目的主機(jī)的IP和port
addrlen:IPV4套接字結(jié)構(gòu)體大小
返回值:
成功:0
失?。?1,并設(shè)置errno
EACCES:權(quán)限不足或被防火墻拒絕
EADDRINUSE:本地地址已被其他套接字使用
ECONNREFUSED:遠(yuǎn)程主機(jī)拒絕連接
ETIMEDOUT:連接超時(shí)
*/
(3)bind函數(shù):服務(wù)器端綁定自己固定的IP和port
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
/*
功能:
給套接字sockfd綁定固定的的IP地址和port
參數(shù):
sockfd:套接字文件描述符
addr:IPV4套接字結(jié)構(gòu)體地址
addrlen:IPV4套接字結(jié)構(gòu)體大小
返回值:
成功:0
失?。?1
*/
(4)listen函數(shù):服務(wù)器端監(jiān)聽(tīng)是否有連接請(qǐng)求
#include<sys/socket.h>
int listen(int sockfd, int backlog);
/*
功能:
監(jiān)聽(tīng)是否有客戶(hù)端請(qǐng)求連接
參數(shù):
sockfd:套接字文件描述符
backlog:已完成連接數(shù)量與未完成連接數(shù)量之和的最大值,一般寫(xiě)128
返回值:
成功:0
失?。?1
*/
(5)accept函數(shù):?從已完成連接隊(duì)列提取連接
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
/*
功能:
從已完成連接隊(duì)列提取連接
參數(shù):
sockfd:套接字文件描述符
addr:IPV4套接字結(jié)構(gòu)體地址,以獲取的客戶(hù)端IP和port信息
addrlen:存儲(chǔ)IPV4套接字結(jié)構(gòu)體大小的變量的地址。
返回值:
成功:新連接socket的文件描述符
失敗:-1
*/
3.3 socket通信示例
(1)TCP客戶(hù)端連接服務(wù)器示例:
#include<arpa/inet.h>
#include<stdio.h>
#include<sys/socket.h>
#include<unistd.h>
int main() {
/* 1.創(chuàng)建socket */
int sock_fd;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
/* 2.連接服務(wù)器 */
// IPV4套接字結(jié)構(gòu)體
struct sockaddr_in addr;
// IPV4
addr.sin_family = AF_INET;
// 服務(wù)器的port,轉(zhuǎn)為網(wǎng)絡(luò)字節(jié)序
addr.sin_port = htons(8888);
// 服務(wù)器IP地址,轉(zhuǎn)為網(wǎng)絡(luò)字節(jié)序存入addr.sin_addr.s_addr
inet_pton(AF_INET, "192.168.0.11", &addr.sin_addr.s_addr);
// 連接
connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
/* 3.讀寫(xiě)數(shù)據(jù) */
char buf[1024] = "";
while (1) {
int n = read(STDIN_FILENO, buf, sizeof(buf)); // 從終端讀入buf
write(sock_fd, buf, n);
n = read(sock_fd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, n);
}
/* 4.關(guān)閉 */
close(sock_fd);
return 0;
}
運(yùn)行結(jié)果:
(2)TCP服務(wù)器端示例:
#include<stdio.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, const char* argv[]) {
/* 1.創(chuàng)建socket */
int lfd = socket(AF_INET, SOCK_STREAM, 0);
/* 2.綁定本機(jī)IP地址和port */
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
// INADDR_ANY為0,表示綁定通配地址,即本地所有IP地址
// addr.sin_addr.s_addr = INADDR_ANY;
inet_pton(AF_INET, "192.168.124.128", &addr.sin_addr.s_addr);
int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0) {
perror("bind");
exit(0);
}
/* 3.監(jiān)聽(tīng) */
listen(lfd, 128);
/* 4.提取 */
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
char ip[16] = "";
printf("新連接到來(lái)!IP:%s, port:%d\n",
inet_ntop(AF_INET, &(cliaddr.sin_addr.s_addr), ip, 16),
ntohs(cliaddr.sin_port));
/* 5.讀寫(xiě) */
char buf[1024] = "";
while (1) {
bzero(buf, sizeof(buf));
int n = read(STDIN_FILENO, buf, sizeof(buf)); // 從終端讀入buf
write(cfd, buf, n);
n = read(cfd, buf, sizeof(buf));
printf("%s", buf);
}
/* 6.關(guān)閉 */
close(lfd);
close(cfd);
return 0;
}
運(yùn)行結(jié)果:
注意:服務(wù)器進(jìn)程被殺死,其占用的端口不會(huì)立即釋放,再次連接會(huì)出現(xiàn)如下錯(cuò)誤:
因?yàn)橄到y(tǒng)防止短時(shí)間內(nèi)頻繁開(kāi)關(guān)相同的端口而將端口設(shè)置為T(mén)IME_WAIT狀態(tài)(一般為2MSL,MSL一般為30s),因此TIME_WAIT大概持續(xù)60s;過(guò)了TIME_WAIT狀態(tài)才可使用該端口。
若要立即使用該端口,可使用端口復(fù)用機(jī)制。
4. 補(bǔ)充:
4.1 recv和send
讀寫(xiě)時(shí),除了使用read、write函數(shù),還可使用recv和send函數(shù),用法類(lèi)似于read和write,flags默認(rèn)寫(xiě)0即可:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-500003.html
#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd, void* buf, size_t len, int flags);
ssize_t send(int sockfd, const void* buf, size_t len, int flags);
4.2 socket包裹函數(shù)
將socket編程常用函數(shù)中的錯(cuò)誤判斷等封裝為函數(shù),使用更方便:?mayueming1/socket-wrap-func: socket常用的包裹函數(shù) (github.com)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-500003.html
到了這里,關(guān)于Linux網(wǎng)絡(luò)編程:socket、客戶(hù)端服務(wù)器端使用socket通信(TCP)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!