socket API和TCP/IP協議學習
阿新 • • 發佈:2018-11-19
socket API和TCP/IP協議學習
- 本部落格用於記錄socket API和TCP/IP協議的學習
- 目標在2017.1.1之前看完unix網路程式設計卷一和TCP/IP詳解卷一
- 本部落格的環境是opensuse Tumbleweed,其他linux發行版可能會有差異
首先放上簡單的server/client程式碼
這兩段程式碼展示了socket連線的一般步驟
- 注意:這個程式碼缺少了相關的標頭檔案,不能直接跑哦!
- 部落格對應完整程式碼見我的github
server.c
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <zconf.h>
#include <stdlib.h>
#include "../unp_prac.h"
/**
* 這是server端的程式,流程總結為:
* 1.函式socket返回listenfd
* 2.設定servaddr(先使用bzero);
* 3.bind,繫結seraddr和listenfd
* 4.listen,將套接字轉換成監聽套接字。
* ---以上socket bind listen 是任何TCP伺服器準備監聽描述符的正常步驟
* 5.無窮迴圈for(;;)
* 6.呼叫accept(等待某個連線到達並被核心接受),
* 在accept中,TCP連線使用所謂的三路握手建立連線,完畢則返回已連線描述符(confd),用於與客戶程序通訊。
* 7.關閉confd,終止連線。
*/
/**
* 編譯:
* gcc -o server server.c ../lib_io/writen.c ../lib/error.c
*/
int main(int argc, char **argv) {
int listenfd, confd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
//socket函式建立一個網際(AF_INET)位元組流(SOCK_STREAM)套接字,這是TCP套接字的花哨名字。
//返回一個小整數的描述符。
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0 )) < 0) {
printf("呼叫socket函式失敗");
}
//設定伺服器
//將servaddr記憶體區域清0;
bzero(&servaddr, sizeof servaddr);
//設定seraddr
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//設為any:如果伺服器有多個網路介面,那麼伺服器程序可以在所有介面上接受使用者連線
servaddr.sin_port = htons(port);/*時間服務的埠為13*/
//bind
if ((bind(listenfd, (SA *) &servaddr, sizeof(servaddr))) < 0) {
printf("\033[31mbind失敗,埠未被系統釋放\n請修改unp_prac.h中的port,然後clean,重新編譯執行\n\033[0m");
exit(0);
};
//listen
listen(listenfd, LISTENQ);
printf("建立監聽連線符,等待連線\n");
while (1) {
confd = accept(listenfd, (SA *) NULL, NULL);
printf("接受連線,返回時間\n");
ticks = time(NULL);
snprintf(buff, sizeof(buff), "\033[35m%.24s\n\033[0m", ctime(&ticks));
Writen(confd, buff, strlen(buff));
close(confd);
}
return 0;
}
client.c
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <zconf.h>
#include "../unp_prac.h"
#include "../lib/inet_pton.h"
/**
* 這是客戶端的程式碼,步驟為下
* 1.socket返回socket描述符
* 2.設定seraddr
* 3.connect
* 4.read socket描述符
*/
/**
* 編譯
* gcc -o client client.c ../lib_io/readn.c ../lib/error.c
*/
int main(int argc, char **argv) {
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
char *servIP;
if (argc != 2) {
servIP = "127.0.0.1";
} else {
servIP = argv[0];
}
//socket函式建立一個網ji(AF_INET)位元組流(SOCK_STREAM)套接字,這是TCP套接字的花哨名字。
//返回一個小整數的描述符。
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("呼叫socket函式失敗");
}
//首先清空servaddr結構體,作用等同於memset
//書上推薦使用bzero是因為只有兩個引數,比較好記
bzero(&servaddr, sizeof servaddr);
servaddr.sin_family = AF_INET;//置地址族為AF_INET
servaddr.sin_port = htons(port);//htons函式負責將整數轉換成二進位制埠號
if ((inet_pton(AF_INET, servIP, &servaddr.sin_addr)) < 0) {
printf("轉換失敗");
}//將ASCII命令列引數轉化為正確的形式
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) {
printf("連線失敗\n");
};
while ((n = Readn(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0;
if (fputs(recvline, stdout) == EOF) {
printf("寫到標準輸出失敗\n");
}
}
if (n < 0) {
printf("讀取失敗\n");
}
}