怎么根據(jù)網(wǎng)站做二維碼seo精華網(wǎng)站
socket類型
流式套接字(SOCK_STREAM) TCP
提供了一個面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯、無重復(fù)的發(fā)送且按發(fā)送順序接收。內(nèi)設(shè)置流量控制,避免數(shù)據(jù)流淹沒慢的接收方。數(shù)據(jù)被看作是字節(jié)流,無長度限制。
數(shù)據(jù)報套接字(SOCK_DGRAM) UDP
提供無連接服務(wù)。數(shù)據(jù)包以獨立數(shù)據(jù)包的形式被發(fā)送,不提供無差錯保證,數(shù)據(jù)可能丟失或重復(fù),順序發(fā)送,可能亂序接收。
原始套接字(SOCK_RAW)
可以對較低層次協(xié)議如IP、ICMP直接訪問。
服務(wù)器:
1.創(chuàng)建流式套接字(socket())------------------------> 有手機
2.指定本地的網(wǎng)絡(luò)信息(struct sockaddr_in)----------> 有號碼
3.綁定套接字(bind())------------------------------>綁定電話
4.監(jiān)聽套接字(listen())---------------------------->待機
5.鏈接客戶端的請求(accept())---------------------->接電話
6.接收/發(fā)送數(shù)據(jù)(recv()/send())-------------------->通話
7.關(guān)閉套接字(close())----------------------------->掛機
客戶端:
1.創(chuàng)建流式套接字(socket())----------------------->有手機
2.指定服務(wù)器的網(wǎng)絡(luò)信息(struct sockaddr_in)------->有對方號碼
3.請求鏈接服務(wù)器(connect())---------------------->打電話
4.發(fā)送/接收數(shù)據(jù)(send()/recv())------------------->通話
5.關(guān)閉套接字(close())--------------------------- >掛機
函數(shù)接口
socket
int socket(int domain, int type, int protocol);
//作用:創(chuàng)建一個socket通信描述符
domain:指定通信的域(通信協(xié)議)AF_UNIX, AF_LOCAL 本地通信AF_INET ipv4AF_INET6 ipv6
type:指定socket的類型SOCK_STREAM:流式套接字,接下來我們的通信使用TCP協(xié)議SOCK_DGRAM:數(shù)據(jù)報套接字,接下來我們的通信使用UDP協(xié)議
protocol:填0 返回值:如果成功,返回創(chuàng)建的描述符,如果失敗,返回-1
connect
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
作用:請求連接服務(wù)器
參數(shù):
sockfd:上面socket接口得到的描述符
addr:相當(dāng)于服務(wù)器的地址(IP+port)
addrlen:地址的長度,因為前面的地址是可變的,所以要通過參數(shù)來協(xié)定地址的長度
返回值:
0 -1
sockaddr結(jié)構(gòu)體
//從bind接口的幫助文檔中拿到
struct sockaddr {sa_family_t sa_family;char sa_data[14];}
上述地址結(jié)構(gòu)是一個通用結(jié)構(gòu),我們在用實際協(xié)議進行通信的時候,需要轉(zhuǎn)換成相應(yīng)協(xié)議的結(jié)構(gòu)體。
用man 7 ip來查看ipv4對應(yīng)的結(jié)構(gòu)體struct sockaddr_in {sa_family_t sin_family; /* 地址協(xié)議族,=socket接口第一個參數(shù) */in_port_t sin_port; /* 指定端口,端口要用網(wǎng)絡(luò)字節(jié)序(大端) */struct in_addr sin_addr; /* IP地址 */
};/* Internet address. */
struct in_addr {uint32_t s_addr; /* address in network byte order */
};
bind
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
作用:綁定服務(wù)器地址:IP和端口,相當(dāng)于對外公開自己的IP和端口,客戶端就可以連接了
addr:綁定的IP和端口結(jié)構(gòu)體,注意綁定的是自己的IP和端口IP:取真實的某個網(wǎng)卡的地址,也可以直接寫0.0.0.0
addrlen:地址的大小
listen
int listen(int sockfd, int backlog);
作用:進入監(jiān)聽狀態(tài),以便接收客戶端的連接
sockfd:上面的服務(wù)器描述符
backlog:同時能處理的客戶端的連接數(shù)量,寫個5 10都可以
返回值:0 -1
accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
接收客戶端連接,注意,這是一個阻塞接口,如果沒有新的連接過來,那么會等待
sockfd:上面的服務(wù)器描述符
addr:客戶端的地址(來電顯示)
addrlen:客戶端地址的長度,是入?yún)?#xff0c;傳入然后可能會被接口修改返回值:如果成功,會返回一個非負的整數(shù),代表接收的新的連接的描述符
recv/send
//recv和send是網(wǎng)絡(luò)的專用接口,比起read和write只是多了一個flags參數(shù),flag一般情況下會取0,代表阻塞接受和發(fā)送
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
返回值:>0:接收的字節(jié)數(shù)<0:失敗=0:代表對端退出了
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
close
int close(int fd);
關(guān)閉套接字連接
TCP
示例代碼
TCP客戶端
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>int main(int argc, char const *argv[])
{//創(chuàng)建套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if(fd < 0){perror("socket err");return -1;}//連接到服務(wù)器struct sockaddr_in server_addr;int len = sizeof(server_addr);//指定連接的服務(wù)器的地址(IP+port)memset(&server_addr, 0, len);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);server_addr.sin_addr.s_addr = inet_addr("192.168.0.109");int ret = connect(fd, (struct sockaddr *)&server_addr, len);if(ret < 0){perror("connect err");return -1;}printf("conect success\n");char buf[64] = {0};while (1){memset(buf, 0, 64);//從終端接收用戶輸入,發(fā)給服務(wù)器,等待服務(wù)器的數(shù)據(jù),打印gets(buf);if(strcmp(buf, "quit") == 0){break;}send(fd, buf, 64, 0);memset(buf, 0, 64);ret = recv(fd, buf, 64, 0);if(ret > 0){printf("recv data = %s\n", buf);}else if(ret == 0){printf("peer exit\n");break;}else{perror("recv err\n");return -1;}}close(fd);return 0;
}
TCP服務(wù)器
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>int main(int argc, char const *argv[])
{//創(chuàng)建服務(wù)器的套接字int server_fd = socket(AF_INET, SOCK_STREAM, 0);if(server_fd < 0){perror("socket err");return -1;}//初始化服務(wù)器地址struct sockaddr_in server_addr, client_addr;int len = sizeof(server_addr);memset(&server_addr, 0, len);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);
#if 0 server_addr.sin_addr.s_addr = inet_addr("192.168.0.194");
#elseserver_addr.sin_addr.s_addr = INADDR_ANY;
#endif//綁定套接字的地址int ret = bind(server_fd, (struct sockaddr *)&server_addr, len);if(ret < 0){perror("bind err");return -1;}//啟動監(jiān)聽ret = listen(server_fd, 5);if(ret < 0){perror("listen err");return -1;}//接收連接int clientfd = accept(server_fd, (struct sockaddr *)&client_addr, &len);if(clientfd < 0){perror("accept err");return -1; }//打印客戶端的地址,注意端口要轉(zhuǎn)成主機字節(jié)序printf("recv a new connect, ip = %s, port=%d\n",\inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));char buf[64] = {0};while (1){memset(buf, 0, 64);//一旦新的連接過來后,后續(xù)通信統(tǒng)統(tǒng)使用新的描述符len = read(clientfd, buf, 64);if(len > 0){printf("recv data = %s\n", buf);}else if(len == 0){printf("client exit\n");break;}else{perror("recv err\n");return -1;}}close(clientfd);close(server_fd);return 0;
}
UDP
TCP和UDP的異同點
相同點:
同屬于傳輸層協(xié)議
不同點:
TCP:流式套接字,面向連接的,可靠的通信
UDP:數(shù)據(jù)報套接字,無連接的,不可靠的通信
通信流程
接口
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
作用:接收UDP對端發(fā)來的消息
sockfd:描述符
buf:接收緩沖區(qū)
len:緩沖區(qū)的長度
flags:填0,阻塞接收
src_addr:收到消息后,對端的地址存到這里(IP+PORT)
addrlen:src_addr緩沖區(qū)長度ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
作用:發(fā)送消息給UDP對端
sockfd:描述符
buf:發(fā)送緩沖區(qū)
len:發(fā)送的長度
flags:填0,阻塞發(fā)送
dest_addr:發(fā)送的目的地址(IP+PORT)
addrlen:dest_addr的長度
PS:UDP的端口和TCP的端口是獨立的!!
代碼示例
UDP服務(wù)器
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#define KB 1024int main(int argc, char const *argv[])
{//創(chuàng)建服務(wù)器的套接字int server_fd = socket(AF_INET, SOCK_DGRAM, 0);if(server_fd < 0){perror("socket err");return -1;}//初始化服務(wù)器地址struct sockaddr_in server_addr, client_addr;int len = sizeof(server_addr);memset(&server_addr, 0, len);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);
#if 0 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
#elseserver_addr.sin_addr.s_addr = INADDR_ANY;
#endif//綁定套接字的地址int ret = bind(server_fd, (struct sockaddr *)&server_addr, len);if(ret < 0){perror("bind err");return -1;}//接收消息,收到客戶端消息,然后把客戶端的消息再返回給客戶端char buf[KB] = {0};struct sockaddr_in cli_addr;socklen_t addrlen = sizeof(struct sockaddr_in);while (1){memset(buf, 0, KB);//服務(wù)器一定是先接收的len = recvfrom(server_fd, buf, KB, 0,(struct sockaddr *)&cli_addr, &addrlen);if(len < 0){perror("recv err");return -1;}printf("recv from %s--%d,data=%s\n", inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port), buf);//接收成功,而且對端地址存儲到了cli_addr中sendto(server_fd, buf, len, 0,(struct sockaddr *)&cli_addr, addrlen);}close(server_fd);return 0;
}
UDP客戶端
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#define KB 1024int main(int argc, char const *argv[])
{//創(chuàng)建套接字int fd = socket(AF_INET, SOCK_DGRAM, 0);if(fd < 0){perror("socket err");return -1;}//連接到服務(wù)器struct sockaddr_in server_addr;int len = sizeof(server_addr);//指定連接的服務(wù)器的地址(IP+port)memset(&server_addr, 0, len);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");char buf[KB] = {0};socklen_t addrlen = sizeof(struct sockaddr_in);while (1){memset(buf, 0, KB);gets(buf);sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, addrlen);memset(buf, 0, KB);len = recvfrom(fd, buf, KB, 0, NULL, NULL);printf("recv from server data = %s\n", buf);}close(fd);return 0;
}