套接字描述符
#include <sys/scoket.h>
int socket(int domain, int type, int protocol);
domain
協議族:
AF_INETipv4
AF_INET6ipv6
AF_UNIX unix域
type
套接字類型
SOCKET_STREAM 流式字節流,tcp時使用這個
SOCKET_DGRAM 固定長度,無連接,不可靠協議 UDP使用這個
SOCKET_SEQPACKET 長度固定,有序可靠,面向連接的報文 ,讀發的字節數是相等的
SOCKET_RAW 允許直接操作IP,需要root權限
type == 0 時使用默認協議 tcp udp,,,
#include <sys/socket.h>
int shutdouwn(int fd, int how);
SHUTDOWN_RD 關閉讀
SHUTDOWN_WR 關閉寫
SHUTDOWN_RDWR 關閉讀寫
相對於close 只有所有引用socket描述符(dup dup2)的都關閉才能關閉文件
shutdown 可以控制socket與 描述符引用無關!
方便控制寫,讀
字節序
由於歷史原因,tcp/ip的字節序是打算字節序
現在很多服務器,pc都是小端序,so爲支持tcp/ip,地址端口必須轉換爲網絡地址序(大端序)
對於數據,只要發送端和接收端都是小端序就沒問題!
#include <arpa/inet.h>
uint32_t htonl(uni32_t hostint32);//ip用這個
uint16_t htons(uni16_t hostint16);//端口用這個
uint32_t ntohl(uni32_t net32);//網絡序轉換成小端序 ip
uint16_t ntohl(uni32_t net16);//網絡序轉換成小端序 端口用
地址格式
struct sockaddr
{
sa_family_t sa_family;
char sa_data[];
...
};
struct in_addr
{
in_addr s_addr;//ipv4的地址
};
struct sockaddr_in
{
sa_family_t sin_family;//地址族
in_port_t sin_port;
struct in_addr sin_addr;//ip地址
};
family ipv4 AF_INET
ipv6 AF_INET6
由於歷史原因,在bind和connect中地址都強制轉換成sockaddr
#include <arpa/inet.h>
const char* inet_ntop(int domain, const void*restrict addr, const char *restrict str, socklen_t bufsize);
socksize str緩衝區大小,防止越界
成功返回轉換後的字符串,失敗返回NULL
int inet_pton(int domain, const char* restrict str, void *restrict addr);
成功返回0,失敗返回-1
轉換後的結果存放在add指向的地址r中
套接字綁定地址
O_NOBLOCK#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
len 表示addr的字節長度
地址端口號必須>=1024,在root權限下可以 < 1024
必須綁定本機的地址
INADDR_ANY可以綁定到本機安裝的所有網卡
可以通過getsockname 獲取fd上綁定的地址,若fd未綁定地址則結果未定義
#include <sys/socket.h>
int getsocketname(int fd, struct sockaddr *restrict addr, socklen_t *alenp);
alenp填充返回的addr的字節數
地址通過addr返回
如果addr緩衝區大小不匹配,自動截斷,不報錯
int getpeername(int fd, struct sockaddr *restrict addr, socklen_t *alenp);
返回鏈接另一端的地址信息
int connect(int sockfd, struct sockaddr *addr, socklen_t len);
客戶端可以向指定的服務器地址嘗試建立連接
若服務器端,未運行可接受連接,則返回-1
對udp也可以用此函數,則此函數會將fd與目的端地址綁定
在linux下如果連接失敗,還可以繼續使用fd再次連接,但在有些系統中,不能再用需要用socket函數產生一個新的fd
若果是爲了可移植性,需要在失敗後在socket()
int listen(int fd, int backlog);
在服務器端用此函數
此函數backlog設置fd,在三次握手未成功,和剛建立連接的隊列的最大排隊值 tcp默認128
int accept(int fd, struct socketaddr *restrict addr, socklen_t len);
用於TCP
監聽連接connect請求,客戶端的地址填充到addr中,len表示addr的字節數
在fd未設置O_NOBLOCK時,阻塞直到連接請求到來
若fd設置了O_NOBLOCK,則返回-1,errno EWOULDBLOCK
成功返回一個新的 socket fd連接到客戶端
tcp中有 用於accept的fd 綁定到本機的ip,用於接受連接請求
還有直接連接客戶端的fd,讀寫的數據都是發送到客戶端,讀客戶端的數據
accept的fd accept成功 返回連接到客戶端的fd
TCP socket的服務器端& 客戶端代碼順序
服務端
int fd = socket()產生一個socketfd
設置地址信息sockaddr_in ip 端口
bind(fd, &addr, len)綁定fd與本地地址
listen 設置可正在握手,和剛握手成功的等待隊列大小
rwfd = accept
讀寫 fd
客戶端
int fd = socket()產生一個socketfd
設置地址信息sockaddr_in ip 端口
connect
用fd讀寫
套接字選項
套接字機制使用了set 和get函數,來獲取和設置socket的
通用套接字選項
套接字層次管理的選項
特定協議選項
int setsockopt(int fd, int level, int op, const void *val, sock_len len);
int getsockopt(int fd, int level, int op, void * val, socklen_t lenp);