基于TCP的客户端、服务器端socket编程
一.实验目的
理解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 }
实验结果: