TCP通訊作為網絡通訊中的一種協議之一有著可靠安全的通訊特點。我們在socket網絡通訊中大量採用TCP協議進行通訊,接下來就簡單介紹一下TCP的socket通訊。
SERVER端:
在進行socket通信之前我們要準備一個套接字(socket)。
int sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)perror(""),exit(-1);
準備地址通訊
struct sockaddr_in{
sin_family;//協議簇基本是PF_INET
short sin_port;//端口號
struct in_addr sin_addr;//IP地址結構體如下
};
struct in_addr{
in_addr_t s_addr;
};
int_addr_t 類型為 uint32_t 底層為 unsignedint
即:
struct in_addr{
unsigned int s_addr;
};
2.1、端口號
概念:是一個邏輯上的整數,這個整數代表一個具有網絡通信功能的進程。
1)端口號的範圍0~65535(unsigned short)。
2)0~1024範圍的端口號已經被系統使用或保留,用戶程序不能使用。
網絡字節順序轉換。
端口號要求轉換成網絡字節順序
A==>本地整數==>網絡字節順序==>網絡==>接收網絡字節數據==>轉換成本地整數==>B
字節順序轉換函數:
#include
htonl htons ntohl ntohs
2.2、 IP地址
2.2.1 可以在網絡上唯一定位一臺計算機。
IP地址的本質是32個(或128個)二進程位.
2.2.2 表示IP地址的方式:
1)點分十進制, 如:192.168.182.53
2)用一個整數:
十六進制: C0B1C30D
2.2.3 轉換
inet_addr 可將點分十進制的字符串IP地址轉換成整數形式。
inet_aton 直接將點分十進制的字符串IP轉換成結構體形式。
inet_ntoa 將結構體IP地址轉換成點分十進制的字符串形式。
綁定
bind(sockfd, (struct sockaddr*)&addr,sizeof(addr));
講soceket套接字與地址綁定在一起。
監聽
listen(sockfd, 100);
監聽函數中第二個參數一般決定了我們連接服務器的客戶端數量。
等待客戶端連接
accept(sockfd, (structsockaddr*)&fromaddr, &len);
Accept函數返回一個新的socket用於接下來新的客戶端與服務器端通信。
與客戶端通訊
通常我們用recv函數和send函數進行通訊,但是這兩個函數必須在識別socket套接字描述符的前提下才能進行接收和發送,為了避免這個弊端,我們也可以用recvfrom和sendto來代替。
CLIENT端:
客戶端與服務器端大體相同,只不過在第3步的時候把綁定函數bind換成connet函數。函數如下:connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
並且客戶端也不需要listen和accept函數。
在我們做完所有的通訊的時候我們要手動的去關閉創建出來的套接字。
close(sockfd);
具體服務器端代碼與客戶端代碼如下:
SERVER:
#include
#include
#include
#include
#include
#include
#include
//tcp服務器端
int main()
{
//1. 準備socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)perror(""),exit(-1);
//2. 準備地址
struct sockaddr_in addr;
addr.sin_family = PF_INET;
addr.sin_port = htons(8888);
inet_aton("192.168.182.53",&addr.sin_addr);
//3.綁定
int res = bind(sockfd,
(struct sockaddr*)&addr, sizeof(addr));
if(res==-1)perror("綁定失敗"),exit(-1);
printf("綁定成功\n");
//4.監聽
res = listen(sockfd, 100);
if(res==-1)perror("監聽失敗"),exit(-1);
printf("啟動監聽\n");
while(1){
//5.等待客戶端連接
struct sockaddr_in fromaddr;
socklen_t len = sizeof(fromaddr);
int fd=accept(sockfd,
(struct sockaddr*)&fromaddr, &len);
//6.和客戶端通信
//6.1接收客戶端數據
char buf[100] = {};
if(recv(fd, buf, sizeof(buf),0)<=0)
perror("接收數據失敗");
else{
printf("客戶端%s說:%s\n",
inet_ntoa(fromaddr.sin_addr),buf);
}
//6.2給客戶端發數據
memset(buf, 0, sizeof(buf));
time_t cur;
time(&cur);
struct tm* cur_tm = localtime(&cur);
sprintf(buf,"%4d-%02d-%02d%02d:%02d:%02d",
cur_tm->tm_year+1900,
cur_tm->tm_mon+1,
cur_tm->tm_mday,
cur_tm->tm_hour,
cur_tm->tm_min,
cur_tm->tm_sec);
send(fd, buf, strlen(buf),0);
//7.關閉socket
close(fd);
}
close(sockfd);
}
CLIENT:
#include
#include
#include
#include
#include
#include
//tcp客戶端
int main()
{
//1. 準備socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)perror(""),exit(-1);
//2. 準備地址
struct sockaddr_in addr;
addr.sin_family = PF_INET;
addr.sin_port = htons(8888);
inet_aton("192.168.182.53",&addr.sin_addr);
//3.連接
int res = connect(sockfd,
(struct sockaddr*)&addr, sizeof(addr));
if(res==-1)perror("連接失敗"),exit(-1);
printf("連接成功\n");
//4.通信
send(sockfd, "您好啊服務器",12, 0);
char buf[100] = {};
recv(sockfd, buf, sizeof(buf), 0);
printf("服務說:%s\n", buf);
//5.關閉
close(sockfd);
}