1.TCP編程模型
tcp特點:基於連接的通信,也就是要想通信必須首先建立連接。
編程模型
服務器 | 客戶機 |
---|---|
創建套接字 | 創建套接字 |
綁定IP地址 | |
監聽端口 | |
等待連接 | 連接服務器 |
收/發數據 | 收/發數據 |
結束連接 | 結束連接 |
- 三次握手含義:客戶機請求連接,服務器回覆連接,連接建立客戶機發送數據。
注意:
- 綁定地址就是將服務器ip地址和端口綁定在一起,也就是這個服務器只用這個固定的端口進行通信。(從系統中選定一個ip地址來與套接字綁定在一起)
- 監聽端口就是監聽綁定ip的這個端口,
2.相關函數
服務器端
創建一個套接字
函數名:socket
函數原型:int socket(int domain,int type,int protocol)
函數功能:創建一個套接字
所屬頭文件:sys/socket.h
返回值:成功返回套接字描述符,失敗返回-1
參數說明:domain:套接字通訊領域, type:套接字類型, protocol:所屬協議綁定地址
函數名:bind
函數原型:int bind(int sockfd,const struct sockaddr *addr,socklen_t len)
函數功能:關聯地址和套接字
所屬頭文件:sys/socket.h
返回值:成功返回0,失敗返回-1
參數說明:sockfd:要關聯套接字的描述符,addr套接字結構的地址,len:套接字的長度
注意:
1.套接字的數據結構
2.字符型的IP地址與整型的IP地址(網絡字節序)之間的轉換
in_addr_t inet_addr(const char *cp)
功能:將字符串形式的IP地址轉換成整數型的IP地址(網絡字節序);
範例:in_addr.saddr = inet_addr(“192.168.1.1”);char *inet_ntoa(struct in_addr)
功能:將整數形式的IP地址轉換成字符型的IP地址。
3.網絡字節序與主機字節序
網絡字節序:即通過網絡發送數據時要把小端模式轉化成大端模式;在本機接收時要把大端模式轉化成本機模式(若本機是大端模式就不需要轉化)
客戶端本機字節序:可能是大端或者小端,要是小端時必須在發送前轉化爲大端。
網絡字節序:在網絡中傳輸的模式,規定爲大端模式
服務器本機字節序:可能是大端或者小端,要是小端時必須在接收時,必須將其(網絡字節序是大端)轉化爲小端
4.主機字節序與網絡字節序的轉換函數
uint32_t htonl(uint32_t hostlong)
功能:將32位的數據從主機字節序轉換爲網絡字節序
範例:in_addr.saddr = htonl(INADDR_ANY)uint_t htons(unit16_t hostshort);
功能:將16位的數據從主機字節序轉化爲網絡字節序
範例:in_addr.saddr = htonl(INADDR_ANY)uint32_t htonl(uint32_t netlong)
功能:將32位的數據從網絡字節序轉換爲主機字節序uint_t htons(unit16_t netlong);
功能:將16位的數據從網絡字節序轉化爲主機字節序設置監聽端口
函數名:listen
函數原型:int listen(int sockfd,int backlog)
函數功能:宣告他願意接收連接請求或者設置服務器可以連接多少個客戶機請求
所屬頭文件:sys/socket.h
返回值:成功返回0,失敗返回-1
參數說明:sockfd:套接字的描述符,backlog:客戶機的數目等待連接
函數名:accept
函數原型:int accept(int sockfd,struct sockaddr addr,socklen_t *len)
函數功能:獲得連接請求並建立連接
所屬頭文件:sys/socket.h
返回值:成功返回新的套接字描述符(後面的發送和接收數據將用這個新的套接字來同信),失敗返回-1
參數說明:sockfd:套接字描述符,addr:客戶機地址(注意:該參數並不是用來傳值的,而是用來收取客戶機的地址的),len:地址長度的地址
注意:該函數會導致服務器的阻塞,也就是當沒有客戶機連接時,服務器一直處於等待狀態。
發送數據
函數名:send
函數原型:int send(int sockfd,const void *buf,size_t nbytes,int flags)
函數功能:發送數據
所屬頭文件:sys/socket.h
返回值:成功返回發送的字節數,失敗返回-1
參數說明:sockfd:套接字描述符,buf:發送字節緩存地址,nbytes:發送字節數,flags:,接收數據
函數名:recv
函數原型:ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags)
函數功能:接收數據
所屬頭文件:sys/socket.h
返回值:成功返回字節長度,若無可用數據或對等方已經按序結束,返回0,若出錯返回-1
參數說明:和send參數含義相同。關閉連接
函數名:close
函數原型:int close(int fd)
函數功能:關閉文件
所屬頭文件:unistd.h
返回值:成功返回0,失敗返回-1
參數說明:fd:所要關閉文件的描述符
客戶機
除了連接服務器函數不一樣,其他函數一樣
- 連接服務器
函數名:connect
函數原型:int connect(int sockfd,const struct sockaddr *addr,socklen_t len)
函數功能:使客戶機與服務器建立連接
所屬頭文件:sys/socket.h
返回值:成功返回0,失敗返回-1
參數說明:sockfd:套接字描述符 ,addr:服務器地址 ,len:地址長度。
3.實例編程
TCP通訊程序設計:實現服務器與客戶機相互發送和接收的功能。
- 服務器程序
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int sockfd;
int n_sockfd;
int fd;
ssize_t num;
char sv_buf[128];
char rv_buf[128];
int sin_size;
unsigned short int portnum = 5000;
struct sockaddr_in sockaddr_sv, sockaddr_ct;
//1.創建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
printf("creat sockfd fail!\n");
exit(1);
}
//2.綁定IP地址
sockaddr_sv.sin_family = AF_INET;
sockaddr_sv.sin_port = htons(portnum);
sockaddr_sv.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&sockaddr_sv.sin_zero,8);
if(-1 == bind(sockfd,(struct sockaddr *) &sockaddr_sv,sizeof(struct sockaddr)))
{
printf("bind port fail!\n");
exit(1);
}
//3.監聽端口
if(-1 == listen(sockfd,5))
{
printf("listen port fail!\n");
exit(1);
}
//4.等待連接
sin_size = sizeof(struct sockaddr);
n_sockfd = accept(sockfd, (struct sockaddr *)&sockaddr_ct,&sin_size);
printf("server get connection from %s\n",inet_ntoa(sockaddr_ct.sin_addr));
//5.發送/接收數據
while (1)
{
fgets(sv_buf,128,stdin);
send(n_sockfd, sv_buf, strlen(sv_buf), 0);
fd = fork();//創建一個進程用來接收信息
if (0 == fd)
{
while(1)
{
num = recv(n_sockfd, rv_buf, 128, 0);
rv_buf[num] = '\0';
printf("%s",rv_buf);
}
}
}
//6.關閉連接
close(sockfd);
}
- 客戶機程序
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int fd;
int sockfd;
int n_sockfd;
ssize_t num = 0;
char ct_buf[128];
char st_buf[128];
//int sin_size;
unsigned short int portnum = 5000;
struct sockaddr_in sockaddr_sv;
//1.創建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
printf("creat sockfd is fail!");
}
//2.等待連接
sockaddr_sv.sin_family = AF_INET;
sockaddr_sv.sin_port = htons(portnum);
sockaddr_sv.sin_addr.s_addr = inet_addr("192.168.1.100");
bzero(&sockaddr_sv.sin_zero,8);
connect(sockfd,(struct sockaddr *)&sockaddr_sv,sizeof(struct sockaddr));
while (1)
{
num = recv(sockfd, ct_buf, 128, 0);
ct_buf[num] = '\0';
printf("%s",ct_buf);
fd = fork();//創建一個進程用來發送信息
if (0 == fd)
{
while(1)
{
fgets(st_buf,128,stdin);
send(sockfd, st_buf, strlen(st_buf), 0);
}
}
}
//3.關閉連接
close(sockfd);
}