服务器与客户端的简单实现
目錄
一、TCP/UDP的區別
二、TCP通信流程:
三、socket地址
四、IP地址轉換函數(字符串ip-整數,主機、網絡字節序的轉換)
五、服務器端和客戶端相關的函數
一、TCP/UDP的區別
UDP:用戶數據協議,面向無連接,可以單播、多播、廣播,面向數據報,不可靠;
TCP:傳輸控制協議,面向連接的,可靠的,基于字節流,僅支持單播傳輸。
| 區別 | UDP | TCP |
| 是否創建連接 | 無連接 | 面向連接 |
| 是否可靠 | 不可靠 | 可靠的 |
| 連接對象的個數 | 一對一、一對多、多對一、多對多 | 僅支持一對一 |
| 傳輸方式 | 面向數據報 | 面向字節流 |
| 首部開銷 | 8個字節 | 最少20個字節 |
| 適用場景 | 實時應用(視頻會議、直播) | 可靠性高的場合(文件傳輸) |
二、TCP通信流程:
服務器端:被動連接的角色
1、創建一個用于監聽的套接字
? ? ? ? -監聽:監聽有客戶端的連接
? ? ? ? -套接字:其實就是一個文件描述符
2、將監聽的文件描述符和與本地的IP和端口號綁定(IP和端口號:服務器的地址信息)?
? ? ? ? -客戶端連接服務器的時候使用的就是這個IP和端口
3、設置監聽,監聽的fd開始工作
4、阻塞等待,當有客戶端發起連接,解除阻塞,接受客戶端的連接,會得到一個和客戶端通信的套接字(fd)
5、通信
? ? ? ? -接收數據
? ? ? ? -發送數據
6、通信結束,斷開連接。
客戶端的通信流程
1、創建一個用于通信的套接字(fd);
2、連接服務器,需要指定連接的服務器的IP和端口;
3、連接成功,客戶端可以直接和服務器通信;
? ? ? ? -接收數據
? ? ? ? -發送數據
4、通信結束,斷開連接。
三、socket地址
sockaddr_in 的結構體:
struct sockaddr_in {sa_family_t sin_family; //地址族(Address Family)一般選擇AF_INETuint16_t sin_port; //16位TCP/UDP端口struct in_addr sin_addr; //32位IP地址char sin_zero[8]; //不使用 };struct int_addr {int_addr_t s_addr;//int 類型 占4個字節 };四、IP地址轉換函數(字符串ip-整數,主機、網絡字節序的轉換)
通常,人們習慣用可讀性好的字符串來表示IP地址,比如用點分十進制字符串表示IPv4地址,以及用十六進制字符串表示IPv6地址。但是編程中我們需要先把他們轉化為整數(二進制數)方能使用。而記錄日志的時候則相反,我們要把整數表示的IP地址轉化為可讀的字符串。下面的3個函數可用于用點分十進制字符串表示的IPv4地址和用網絡字節序整數表示的IPv4地址之間的轉換。
#include <arpa/inet.h> int inet_pton(int af,const char *src,void *dst); // p:點分十進制IP字符串,n:network,網絡字節序的整數 -af:地址族 AF_INET AF_INET6 -src:需要轉換的點分十進制的IP字節串 -dst:轉換后的結果保存在這個里面 char *inet_ntop(int af,const void *src,char *dst,socklen_t size); -af:地址族,AF_INET AF_INET6 -src:網絡字節序整數存放的地方 -dst:要轉換的ip地址字符串保存的地方 -size:第三個參數的大小(數組的大小) -返回值:返回轉換后的數據的地址(字符串),和dst是一樣的?用代碼舉個例子
1 #include <stdio.h>2 #include <arpa/inet.h>3 int main()4 {5 //1、創建一個ip字符串,點分十進制IP字符串6 char buf[]="192.168.17.110";7 unsigned int num=0;8 inet_pton(AF_INET,buf,&num);//將點分十進制的IP字符串轉換成網絡字節序9 10 unsigned char *p=(unsigned char *)#11 printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));12 13 //將網絡字節序的IP整數轉換成點分十進制字符串14 char ip[16]="";15 const char *str=inet_ntop(AF_INET,&num,ip,16);16 printf("str:%s\n",str);17 printf("ip:%s\n",ip);18 return 0;19 20 }?輸出結果為:
?由結果可知,按照這樣的使用規則是正確的。
五、服務器端和客戶端相關的函數
(1)服務器端
1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <string.h>5 #include <arpa/inet.h>6 int main()7 {8 //1、創建socket(用于監聽的套接字)9 int lfd=socket(AF_INET,SOCK_STREAM,0);10 if(lfd==-1)11 {12 perror("socket");13 exit(-1);14 }15 //2、綁定16 struct sockaddr_in saddr;17 saddr.sin_family = AF_INET;18 //inet_pton(AF_INET,"192.168.17.134", saddr.sin_addr.s_addr);19 saddr.sin_addr.s_addr=0;//0.0.0.0或者INADDR_ANY20 saddr.sin_port=htons(9999);21 int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));22 if(ret==-1)23 {24 perror("bind");25 exit(-1);26 }27 //3、監聽28 ret=listen(lfd,8);//同時能接收8個連接29 if(ret==-1)30 {31 perror("listen");32 }33 //4、接收客戶端連接34 struct sockaddr_in clientaddr;35 socklen_t len=sizeof(clientaddr);36 int num=accept(lfd,(struct sockaddr *)&clientaddr,&len);37 if(num==-1)38 {39 perror("accept");40 exit(-1);41 }42 //輸出客戶端信息43 char clientIP[16];44 inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));45 unsigned short clientPort = ntohs(clientaddr.sin_port);46 printf("client ip is %s,port is %d\n",clientIP,clientPort);47 //5、獲取客戶端的數據48 //給客戶端發送數據49 char recvBuf[1024]={0};while(1){50 int fd=read(num,recvBuf,sizeof(recvBuf));51 if(fd==-1)52 {53 perror("read");54 exit(-1);55 }56 else if(fd>0)57 {58 printf("recv client data :%s\n",recvBuf);59 }60 else if(len==0)61 {62 //表示客戶端斷開連接63 printf("client close...");64 }65 char *data="hello , i am server";66 write(num,data,strlen(data));}67 close(lfd);68 69 return 0;70 }(2)客戶端
1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <string.h>5 #include <arpa/inet.h>6 int main()7 {8 //1、創建socket9 int lfd=socket(AF_INET,SOCK_STREAM,0);10 if(lfd==-1)11 {12 perror("socket");13 exit(-1);14 }15 //2、連接服務器16 struct sockaddr_in serveraddr;17 serveraddr.sin_family=AF_INET;18 inet_pton(AF_INET,"192.168.17.134",&serveraddr.sin_addr.s_addr);19 serveraddr.sin_port=htons(9999);20 int ret=connect(lfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));21 if(ret==-1)22 {23 perror("connect");24 exit(-1);25 }26 //3、通信27 //給客戶端發送數據28 char recvBuf[1024]={0};29 while(1)30 {31 char *data="hello , i am client";32 write(lfd,data,strlen(data)+1);33 sleep(1);34 //接收服務器發來的數據 35 int len=read(lfd,recvBuf,sizeof(recvBuf));36 if(len==-1)37 {38 perror("read");39 exit(-1);40 }41 else if(len>0)42 {43 printf("recv server data:%s\n",recvBuf);44 }45 else if(len==0)46 {47 //與服務器斷開48 printf("server closed ...\n");49 }50 }51 //關閉文件描述符52 close(lfd);53 // close(len);54 55 56 57 return 0;58 }然后開始運行,運行時一定要先運行服務器端,再運行客戶端,否則會報錯。
服務器端運行結果為:?
客戶端運行結果為:?
我在寫服務器端的時候遇到了一個小插曲,就是我在寫read()函數和read()函數時,把第一個位置寫成了socket的返回值,發現客戶端一直不能接收來自服務端的消息,后來我把這個地方改成?accept()函數的返回值后就一切正常了。
真的受教了!
總結
以上是生活随笔為你收集整理的服务器与客户端的简单实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: smtplib python教程_Pyt
- 下一篇: 计算机一级繁体字转换,繁体字转换器