2017年8月7日 星期一

1 基本介紹及參考資料

參考:
http://kezeodsnx.pixnet.net/blog/post/27462696-socket-programming-%E7%AD%86%E8%A8%98
http://beej-zhtw.netdpi.net/05-system-call-or-bust/5-2-socket--get-file-descriptor
http://www.tenouk.com/cnlinuxsockettutorials.html


TCP&UDP 基本區別

TCP
優點:
可靠性、順序性

缺點:
建立連線 速度比較慢。

tcp 可靠性確保=>

UDP
優點:
傳輸速度快

缺點:
不可靠 可能資料lost 看網卡buffer

TCP:

UDP



基本指令


  • socket:


int socket(int domain, int type, int protocol);

domain所以這裡只會用到 IPv4 的 PF_INET 及 IPv6 的 PF_INET6。
type可靠 TCP socket 的 SOCK_STREAM(send()recv()
不可靠快速 UDP socket 之 SOCK_DGRAM(sendto()recvfrom()
[另一個有趣的 socket type 是 SOCK_RAW,這個可以用來手動建構封包表頭]
protocolprotocol 參數指定在特定的 socket type 要用的 protocol(通訊協定),正如我所說的,比如:SOCK_STREAM 使用 TCP。在使用 SOCK_STREAM 或 SOCK_DGRAM 時,你可以直接將 protocol 設定為zero,這樣它會自動使用適合的 protocol,要不然你可以用 getprotobyname() 查詢適合的協定編號(protocol number)。


struct addrinfo 
{
       int              ai_flags;
       int              ai_family;
       int              ai_socktype;
       int              ai_protocol;
       socklen_t        ai_addrlen;
       struct sockaddr *ai_addr;
       char            *ai_canonname;
       struct addrinfo *ai_next;
};




参数
取值
说明
ai_family
AF_INET
2
IPv4
AF_INET6
23
IPv6
AF_UNSPEC
0
协议无关
ai_protocol
IPPROTO_IP
0
IP协议
IPPROTO_IPV4
4
IPv4
IPPROTO_IPV6
41
IPv6
IPPROTO_UDP
17
UDP
IPPROTO_TCP
6
TCP
ai_socktype
SOCK_STREAM
1
SOCK_DGRAM
2
ai_flags
AI_PASSIVE
1
被动的,用于bind,通常用于server socket
AI_CANONNAME
2
用于返回主机的规范名称
  
AI_NUMERICHOST
4
地址为数字串



程式碼:
getaddrinfo(NULL, "3490", &hints, &res);

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

getaddrinfo=> 如www.google.com =>172.217.27.132-443

source path=>google drive/Book CS/Socket/SocketGetaddrinfo
https://drive.google.com/file/d/1WB1L05am7DGPxLLxHhYW-Nwptg60p4zI/view?usp=sharing

  • Bind


int bind(int socketfd, struct sockaddr *my_addr, socklen_t addrlen);


struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。
二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。

return:
On success, zero is returned. On error, -1 is returned

程式碼:

bind(sockfd, res->ai_addr, res->ai_addrlen);

p.s. 不要bind same port

  • Listen


int listen(int socketfd, int backlog);

參數 backlog 代表在 kernel 開始拒絕新連線以前,你可以有多少的連線在等待。所以當新的連線進入時,你應該盡快的用 accept() 來處理,讓 backlog 不會滿出來。

return:

On success, zero is returned. On error, -1 is returned

程式碼:

listen(sockfd, 10);

  • Accept

int accept(int socketfd, struct sockaddr *addr, socklen_t *addrlen);

原本用來 listen 的 socket 仍然還是會留著,當有新連線進來時,一樣是用 accept() call 來接受新的連線。

addr這裡會填入連線到server這裡的 client 位址。
addrlen這裡會填入 addr 參數中傳回的資料結構大小。

程式碼:

struct sockaddr_storage their_addr;
int addr_size=sizeof(struct sockaddr_in);
int  new_fd = accept(sockfd, (struct sockaddr *)&their_addr, (socklen_t*)&addr_size);

return:
On success, these system calls return a file descriptor for the accepted socket. On error, -1 is returned,

  • Connect

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);


填入 serv_addr , 要連接的server



return:

On success, zero is returned. On error, -1 is returned

  • recv

ssize_t recv(int s, void *buf, size_t len, int flags);
ssize_t recvfrom(int s, void *buf, size_t len, int flags,
                 struct sockaddr *from, socklen_t *fromlen);

TCP SOCK_STREAM socket 使用 recv() 、UDP SOCK_DGRAM socket 使用 recvfrom()。


recv(sockfd, buf, len, flags);
is equivalent to
recvfrom(sockfd, buf, len, flags, NULL, NULL);


flags is zero 表示 不包含任何flag


p.s. recv vs read

  • send

ssize_t send(int s, const void *buf, size_t len, int flags);
ssize_t sendto(int s, const void *buf, size_t len,
               int flags, const struct sockaddr *to,
               socklen_t tolen);

send() 用在需連線的 TCP SOCK_STREAM socket,而 sendto() 用在免連線的 UDP SOCK_DGRAM socket。



flags is zero 表示 不包含任何flag

p.s. send vs write



flag可參考:
http://www.mit.edu/afs.new/athena/system/i386_deb50/os-ubuntu-9.04/usr/include/bits/socket.h


  • recv vs read

With a zero flags argument, recv() is generally equivalent to read().
只有在datagram 時 ,收到長度為0的pending,recv 會drop,read會保留.




  • send vs write

write():
On success, the number of bytes written are returned (zero indicates nothing was written). On error, -1 is returned, and errno is set appropriately. If count is zero and the file descriptor refers to a regular file, 0 will be returned without causing any other effect. For a special file, the results are not portable.
send():
The calls return the number of characters sent, or -1 if an error occurred.

因為send 可能長度太長失敗
If the message is too long to pass atomically through the underlying protocol, 
the error EMSGSIZE is returned, and the message is not transmitted.

https://stackoverflow.com/questions/9048959/write-and-send-solving-errors-difference


*另外注意

"recv byte & 次數"  與  "send &次數" 不一定對等舉例,
send 3000byte 一次, recv buffer 調5000byte (也並非一次一定收到3000byte)
send 100byte 3次 , recv 可能一次收到300byte.