一、Socket是什麼
1、 socket套接字:
socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。Socket就是該模式的一個實現, socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉).
說白了Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
注意:其實socket也沒有層的概念,它只是一個facade設計模式的應用,讓編程變的更簡單。是一個軟件抽象層。在網絡編程中,我們大量用的都是通過socket實現的。
2, 套接字的描述符是什麼
其實在內核中我們進行套接字通信的時候開闢了兩塊空間用於我們數據的發送和接受,我們在創建套接字的時候其實操作的也時文件描述符。
3,什麼時網絡字節序
- 字節序是cpu在內存中進行數據存儲的順序,這可以分爲大端字節序,小端字節序。(這是取決於cpu架構)
大端字節序:低地址存高位。
小端字節序:低地址存低位。
主機字節序:當前計算機的字節序
可以想象在兩臺不同的計算機因爲計算機的字節序不同而導致我們數據的二義性,這使得我們收到的數據和發送的數據不一致。
這時候我們使用同一的規定,網絡字節序(就是在網絡傳輸中規定使用同一種字節序,這裏是大端字節序)。
地址轉換
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
判斷當前機器的大小端
#include <iostream>
using namespace std;
int main(){
int p = 0x11223344;
char* pp = (char*)&p;
cout << *pp << endl;
system("pause");
return EXIT_SUCCESS;
}
4,套接字編程API
- socket接口
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:域參數指定通信域(常使用AF_INET)
type:套接字類型(通常使用TCP的SOCK_STREAM和udp的SOCK_DGRAM)
protocol:協議類型,常使用IPPROTO_TCP(也就是6),IPPROTO_UDP(17)。如果是0的話使用默認協議
返回值:生成一個套接字描述符,失敗返回-1並設置errno
- bind接口
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
bind
爲套接字 sockfd
指定本地地址 my_addr
. my_addr
的長度爲addrlen
(字節).傳統的叫法是給一個套接字分配一個名字. 當使用 socket(2),函數創建一個套接字時,它存在於一個地址空間(地址族), 但還沒有給它分配一個名字
成功返回0,失敗返回-1
- listen接口
#include <sys/socket.h>
int listen(int s, int backlog);
使其能夠自動接收到來的連接並且爲連接隊列指定一個長度限制.注意是同時請求連接的最大數量
s:是套接字描述符
backlog:數量
返回值:成功返回0,失敗返回-1.
Listen實現最大連接原理
在內核中存在兩個隊列,一個是未完成連接和已完成連接,而最大連接數量就是未完成連接隊列的最大節點數
- connect接口
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
客戶端請求連接接口
sockfd:是套接字描述符
addr:服務端地址信息
addrlen:地址信息變量的長度
返回值:成功返回0,失敗返回-1;並設置errno
- accept接口
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
s:套接字描述符
addr:用來保存客戶端的地址信息
addrlen:變量的長度
返回值:失敗返回-1,成功返回用於通信的套接字描述符
- 發送信息接口
#include <sys/types.h>
#include <sys/socket.h>
int send(int s, const void *msg, size_t len, int flags);
int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
int sendmsg(int s, const struct msghdr *msg, int flags);
send
, sendto
, 和 sendmsg
用於向另一個套接字傳遞消息. send
僅僅用於連接套接字,而 sendto
和 sendmsg
可用於任何情況下.
所以在tcp通信中使用send,在udp中使用sendto
s
:用於通信的套接字描述符
msg
:發送信息的緩衝區
len
:長度
flags
:屬性選項設置
const struct sockaddr *to, socklen_t tolen
:這只是在sendto
中使用,需要指定地址信息
- 接受信息接口
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
recv,recvfrom,recvmsg其實和send,sendto,sendmsg類似。
理解sockaddr,sockaddr_in
其實sockaddr
和sockaddr_in
地址佔用的是同樣的大小,只是在先前unix階段我們使用的是sockaddr
,所以在接口的設計中使用的是sockaddr
,但是在sockaddr
中我們不能很好的對空間進行賦值,所以我們要使用sockaddr_in
進行賦值然後進行強轉