linux 編程之 socket
TCP/IP協議及socket封裝
socket編程的基本流程
socket連接的建立(3次握手)
socket連接的斷開(3次握手)
由于TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務后就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
(1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送(報文段4)。
(2)服務器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將占用一個序號。
(3)服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A(報文段6)。
(4)客戶端A發回ACK報文確認,并將確認序號設置為收到序號加1(報文段7)。
對應函數接口如圖:
socket編程之bind函數
int bind(int sockfd, const struct sockaddr *addr,socklen_t *addrlen);
功能描述:
當用socket()函數創建套接字以后,套接字在名稱空間(網絡地址族)中存在,但沒有任何地址給它賦值。bind()把用 addr 指定的地址賦值給用文件描述符代表的套接字 sockfd 。 addrlen 指定了以 addr 所指向的地址結構體的字節長度。一般來說,該操作稱為“給套接字命名”。
通常,在一個 SOCK_STREAM 套接字接收連接之前,必須通過bind()函數用 本地地址 為套接字命名。
備注:
調用bind()函數之后,為socket()函數創建的套接字關聯一個相應地址,發送到這個地址的數據可以通過該套接字讀取與使用。
備注:
bind()函數并不是總是需要調用的,只有用戶進程想與一個具體的地址或端口相關聯的時候才需要調用這個函數。如果用戶進程沒有這個需要,那么程序可以依賴內核的自動的選址機制來完成自動地址選擇,而不需要調用bind()函數,同時也避免不必要的復雜度。在一般情況下,對于服務器進程問題需要調用bind()函數,對于客戶進程則不需要調用bind()函數。
socket編程之accept函數
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
功能參數描述
accept()系統調用主要用在基于連接的套接字類型,比如SOCK_STREAM和SOCK_SEQPACKET。它 提取出所監聽套接字的等待連接隊列中第一個連接請求 , 創建一個新的套接字,并返回指向該套接字的文件描述符 。新建立的套接字不在監聽狀態,原來所監聽的套接字也不受該系統調用的影響。
備注:
新建立的套接字準備發送send()和接收數據recv()。
參數:
sockfd , 利用系統調用socket()建立的套接字描述符,通過bind()綁定到一個本地地址(一般為服務器的套接字),并且 通過listen()一直在監聽連接 ;
服務器代碼:
#include <stdio.h>
include <stdlib.h>
include <sys/types.h>
include <sys/socket.h>
include <netinet/in.h>
include <arpa/inet.h>
include <unistd.h>
include <string.h>
include <fcntl.h>
include <sys/shm.h>
define QUEUE 20
define MYPORT 8887
define BUFF_SIZE 1024
int main(int argc, char argv[])
{
int server_sockfd = socket(AF_INET, SOCK_STREAM, 0); // 定義socket fd
// 定義sockaddr_in
struct sockaddr_inserver_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(MYPORT); // host to network short
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);// host to network long
// bind socket address to socket fd
if (bind(server_sockfd, (struct sockaddr)&server_sockaddr, sizeof(server_sockaddr)) == -1) {
perror("bind");
exit(1);
}
if (listen(server_sockfd, QUEUE) == -1) {
perror("listen");
exit(2);
}
// 客戶端套接字
char buff[BUFF_SIZE];
struct sockaddr_inclient_addr;
socklen_tlength = sizeof(client_addr);
// 監聽并返回客戶端fd
int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);
if (conn < 0) {
perror("accept");
exit(3);
}
while (1) {
memset(buff, 0, sizeof(buff));
int len = recv(conn, buff, sizeof(buff), 0);
if (strcmp(buff, "exit\n") == 0)
break;
printf("server received:%s", buff);
send(conn, buff, len, 0);
}
close(conn);
close(server_sockfd);
return 0;
}
</code></pre>
客戶端代碼:
#include <sys/types.h>
include <sys/socket.h>
include <stdio.h>
include <netinet/in.h>
include <arpa/inet.h>
include <unistd.h>
include <string.h>
include <stdlib.h>
include <fcntl.h>
include <sys/shm.h>
define MYPORT 8887
define BUFF_SIZE 1024
int main()
{
int sock_client_fd = socket(AF_INET, SOCK_STREAM, 0);
// 定義server socket
struct sockaddr_inserver_addr;
memset(&server_addr, 0, sizeof(0));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(MYPORT);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服務器ip
// 連接服務器
if (connect(sock_client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
exit(1);
}
char sendbuff[BUFF_SIZE], recvbuff[BUFF_SIZE];
while (fgets(sendbuff, sizeof(sendbuff), stdin) != NULL){
send(sock_client_fd, sendbuff, strlen(sendbuff), 0); // 發送
if (strcmp(sendbuff, "exit\n") == 0) break;
recv(sock_client_fd, recvbuff, sizeof(recvbuff), 0);
printf("client received:%s", recvbuff);
memset(recvbuff, 0, sizeof(recvbuff));
memset(sendbuff, 0, sizeof(sendbuff));
}
close(sock_client_fd);
return 0;
}
</code></pre>
來自:http://blog.jobbole.com/107743/