Linux關於socket(TCP協議實現C/S結構)
socket概述
為了簡化開發通訊程式的工作,由Berkely學校開發了一套網路通訊程式的API函式標準 socket標準被擴充套件成window socket和unix socket linux中的網路程式設計通過socket介面實現。Socket既是一種特殊的IO,它也是一種檔案描述符。一個完整的Socket 都有一個相關描述{協議,本地地址,本地埠,遠端地址,遠端埠};每一個Socket 有一個本地的唯一Socket 號,由作業系統分配。
TCP三次握手
1、初始化主機通過一個同步標誌置位的資料段發出會話請求。 2、接收主機通過發回具有以下專案的資料段表示回覆:同步標誌置位、即將傳送的資料段的起始位元組的順序號、應答並帶有將收到的下一個資料段的位元組順序號。 3、請求主機再回送一個數據段,並帶有確認順序號和確認號
socket之流式套接字(TCP)
流式套接字(SOCK_STREAM) 流式的套接字可以提供可靠的、面向連線的通訊流。它使用了TCP協議。TCP 保證了資料傳輸的正確性和順序性
socket之C/S實現(TCP)
socket模擬客戶端
以下是實現程式碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #include <netinet/in.h> /* for struct sockaddr_in*/ #include <sys/errno.h> #include <signal.h> //關於客戶端的socket void error_exit(char *name) { perror(name); exit(-1); } int main(int argc,char *argv[]) { if(argc<3) { printf("run program+ip+port\n"); return-1; } int sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0) { error_exit("create error"); } //連線伺服器,設定伺服器的地址(ip和埠) struct sockaddr_in svraddr; memset(&svraddr,0,sizeof(svraddr)); svraddr.sin_family=AF_INET; svraddr.sin_addr.s_addr= inet_addr(argv[1]); svraddr.sin_port=htons(atoi(argv[2])); int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr)); if(ret<0) { error_exit("connect error"); } char buf[1024]={0}; int rdsize =read(sockfd,buf,1024); printf("read size %d,data=%s\n",rdsize,buf); return 0; }
socket模擬服務端
以下實現程式碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #include <netinet/in.h> /* for struct sockaddr_in*/ #include <sys/errno.h> #include <signal.h> //關於伺服器的socket void error_exit(char *name) { perror(name); exit(-1); } int main(int argc,char *argv[]) { int sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0) { error_exit("create error"); } //繫結地址(ip和埠號) struct sockaddr_in svraddr; memset(&svraddr,0,sizeof(svraddr)); svraddr.sin_family=AF_INET; svraddr.sin_addr.s_addr=INADDR_ANY; //svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");//第二種寫法 svraddr.sin_port=htons(5555);//大於1024,系統用的都是小於1024 int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr)); if(ret<0) { error_exit("bind error"); } //設定監聽引數back login,半連線數的最大 ret=listen(sockfd,1024); if(ret<0) { error_exit("listen error"); } struct sockaddr_in removeaddr; int addr_len=sizeof(removeaddr);//一定要是長度,不能為0 while(1) { //返回一個和客戶端通訊的檔案描述符 int fd=accept(sockfd,(struct sockaddr*)&removeaddr,&addr_len); //TCP讀寫規則 //1.讀的時候沒有資料,進去阻塞 //2.如果關閉,讀取返回0 //3.如果關閉,寫入資料會產生SIGPIPE int wdsize=write(fd,"helloworld",strlen("helloworld")); printf("write size=%d\n",wdsize); } return 0; }
好了,實現以上程式碼,我們進行結果實現:
我們先執行服務端程式碼,正在寫入中......
接下來執行客服端程式碼,第一個引數為我們的ip號,5555為我們設定的埠
最後服務端顯示:
下面我們來驗證一下流式套接字的特點
socket之流式套接字是這樣描述的,流式套接字(SOCK_STREAM) 流式的套接字可以提供可靠的、面向連線的通訊流。它使用了TCP協議。TCP 保證了資料傳輸的正確性和順序性
上面這句話的意思其實是說流式套接字傳輸資料的時候其實像管道一樣,資料像流水一樣,也就是粘包
我們將伺服器的程式碼寫入的時候,連續寫入三次helloworld
以下完整程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h> /* for struct sockaddr_in*/
#include <sys/errno.h>
#include <signal.h>
//關於伺服器的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//繫結地址(ip和埠號)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr=INADDR_ANY;
//svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");//第二種寫法
svraddr.sin_port=htons(5555);//大於1024,系統用的都是小於1024
int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("bind error");
}
//設定監聽引數back login,半連線數的最大
ret=listen(sockfd,1024);
if(ret<0)
{
error_exit("listen error");
}
struct sockaddr_in removeaddr;
int addr_len=sizeof(removeaddr);//一定要是長度,不能為0
while(1)
{
//返回一個和客戶端通訊的檔案描述符
int fd=accept(sockfd,(struct sockaddr*)&removeaddr,&addr_len);
//TCP讀寫規則
//1.讀的時候沒有資料,進去阻塞
//2.如果關閉,讀取返回0
//3.如果關閉,寫入資料會產生SIGPIPE
int wdsize=write(fd,"helloworld",strlen("helloworld"));
wdsize=write(fd,"helloworld",strlen("helloworld"));
wdsize=write(fd,"helloworld",strlen("helloworld"));
printf("write size=%d\n",wdsize);
}
return 0;
}
我們將客服端的程式碼加一個sleep,讀的時候延遲一下
以下完整程式碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h> /* for struct sockaddr_in*/
#include <sys/errno.h>
#include <signal.h>
//關於客戶端的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("run program+ip+port\n");
return-1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//連線伺服器,設定伺服器的地址(ip和埠)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr= inet_addr(argv[1]);
svraddr.sin_port=htons(atoi(argv[2]));
int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("connect error");
}
sleep(1);
char buf[1024]={0};
int rdsize =read(sockfd,buf,1024);
printf("read size %d,data=%s\n",rdsize,buf);
return 0;
}
跟上面的步驟執行一遍,得以下結果
我們發現三個helloworld是連續的,這就驗證了流式套接字的特點,如同管道一般傳輸資料