一.實驗目的
理解tcp傳輸客戶端服務器端通信流程
二.實驗平台
MAC OS
三.實驗內容
編寫TCP服務器套接字程序,程序運行時服務器等待客戶的連接,一旦連接成功,則顯示客戶的IP地址、端口號,並向客戶端發送字符串。
四.實驗原理
使用TCP套接字編程可以實現基於TCP/IP協議的面向連接的通信,它分為服務器端和客戶端兩部分,其主要實現過程如下

四.實驗流程
服務器端流程
1.創建socket
socket是一個結構體,被創建在內核中
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp協議
2.調用bind函數
將socket和地址(包括ip、port)綁定。
需要定義一個結構體地址,以便於將port的主機字節序轉化成網絡字節序
struct sockaddr_in serveraddr; //地址結構體
bind函數
bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))
3.listen監聽,將接收到的客戶端連接放入隊列
listen(sockfd,10) //第二個參數是隊列長度
4.調用accept函數,從隊列獲取請求,返回socket描述符
如果無請求,將會阻塞,直到獲得鏈接
int fd=accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);
5.調用IO函數和客戶端雙向通信
6.關閉accept返回的socket
close(fd);
服務器端代碼:
1 #include "iostream" 2 #include "netdb.h" 3 #include "stdio.h" 4 #include "stdlib.h" 5 #include "sys/socket.h" 6 #include "unistd.h" 7 #include "arpa/inet.h" 8 #include "string.h" 9 #include "memory.h" 10 #include "signal.h" 11 #include "time.h" 12 13 int sockfd; 14 15 void sig_handler(int signo) 16 { 17 if(signo==SIGINT) 18 { 19 printf("Server close \n"); 20 close(sockfd); 21 exit(1); 22 } 23 } 24 25 //輸出連接上來的客戶端相關信息 26 void out_addr(struct sockaddr_in *clientaddr) 27 { 28 //將端口從網絡字節序轉成主機字節序 29 int port =ntohs(clientaddr->sin_port); 30 char ip[16]; 31 memset (ip,0,sizeof(ip)); 32 inet_ntop(AF_INET, 33 &clientaddr->sin_addr.s_addr,ip,sizeof(ip)); 34 printf("client:%s(%d)connected\n",ip,port); 35 } 36 37 void do_service(int fd) 38 { 39 //獲取系統時間 40 long t=time(0); 41 char *s=ctime(&t); 42 size_t size=strlen(s)*sizeof(char); 43 //將服務器端的系統時間寫到客戶端 44 if(write(fd,s,size)!=size) 45 { 46 perror("write error"); 47 } 48 } 49 50 int main(int argc,char *argv[]) 51 { 52 if(argc<2) 53 { 54 printf("usage:%s #port\n",argv[0]); 55 exit(1); 56 } 57 58 if(signal(SIGINT,sig_handler)==SIG_ERR) 59 { 60 perror("signal sigint error"); 61 exit(1); 62 } 63 64 /*1. 創建socket 65 AF_INT:ipv4 66 SOCK_STREAM:tcp協議 67 */ 68 sockfd=socket(AF_INET,SOCK_STREAM,0); 69 if(sockfd<0){ 70 perror("socket error"); 71 exit(1); 72 } 73 74 /*2:調用bind函數綁定socket和地址*/ 75 76 struct sockaddr_in serveraddr; 77 memset(&serveraddr,0,sizeof(serveraddr)); 78 //往地址中填入ip,port,internet類型 79 serveraddr.sin_family=AF_INET; //ipv4 80 serveraddr.sin_port=htons(atoi(argv[1])); //htons主機字節序轉成網絡字節序 81 82 serveraddr.sin_addr.s_addr=INADDR_ANY; 83 84 if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0) 85 { 86 perror("bind error"); 87 exit(1); 88 89 } 90 91 /*3:調用listen函數監聽(指定port監聽) 92 通知操作系統區接受來自客戶頓的連接請求 93 第二個參數:指定隊列長度 94 */ 95 96 if(listen(sockfd,10)<0) 97 { 98 perror("listen error"); 99 100 } 101 102 /*4:調用accept函數從隊列中獲得一個客戶端的請求連接 103 */ 104 105 struct sockaddr_in clientaddr; 106 socklen_t clientaddr_len=sizeof(clientaddr); 107 108 while(1){ 109 int fd=accept(sockfd, 110 (struct sockaddr*)&clientaddr, 111 &clientaddr_len); 112 if(fd<0){ 113 perror("accept error"); 114 continue; 115 } 116 117 /*5:調用IO函數(read/write)和 118 連接的客戶端進行雙向通信 119 */ 120 out_addr(&clientaddr); 121 do_service(fd); 122 123 /*6.關閉socket*/ 124 close(fd); 125 } 126 127 return 0; 128 }
客戶端代碼:
1 #include "netdb.h" 2 #include "sys/socket.h" 3 #include "stdio.h" 4 #include "stdlib.h" 5 #include "string.h" 6 #include "memory.h" 7 #include "unistd.h" 8 #include <arpa/inet.h> 9 10 int main(int argc,char *argv[]) 11 { 12 if(argc<3) 13 { 14 printf("usage:%s ip port \n",argv[0]); 15 exit(1); 16 } 17 18 /*步驟1:創建socket*/ 19 int sockfd=socket(AF_INET,SOCK_STREAM,0); 20 if(sockfd<0) 21 { 22 perror("socket error"); 23 exit(1); 24 } 25 26 struct sockaddr_in serveraddr; 27 memset(&serveraddr,0,sizeof(serveraddr)); 28 serveraddr.sin_family=AF_INET; 29 serveraddr.sin_port=htons(atoi(argv[2])); 30 31 //主機字節序轉換成網絡字節序 32 inet_pton(AF_INET,argv[1], 33 &serveraddr.sin_addr.s_addr); 34 35 /*步驟2:客戶端調用connect函數連接到服務器 36 37 */ 38 if(connect(sockfd,(struct sockaddr*)&serveraddr, 39 sizeof(serveraddr))<0) 40 { 41 perror("connect error"); 42 exit(1); 43 } 44 45 /*步驟3:調用IO函數(read/write)和服務器端雙向通信*/ 46 char buffer[1024]; 47 memset(buffer,0,sizeof(buffer)); 48 size_t size; 49 50 if((size=read(sockfd, 51 buffer,sizeof(buffer)))<0) 52 { 53 perror("read error"); 54 } 55 56 if(write(STDOUT_FILENO,buffer,size)!=size) 57 { 58 perror("write error"); 59 } 60 }
實驗結果:

