基于TCP的客户端、服务器端socket编程
2017-12-13 22:35
330 查看
基于TCP的客户端、服务器端socket编程
一、实验目的
理解tcp传输客户端服务器端通信流程二、实验平台
MAC OSgxx-include-dir=/usr/include/c++/4.2.1
三、实验内容
编写TCP服务器套接字程序,程序运行时服务器等待客户的连接,一旦连接成功,则显示客户的IP地址、端口号,并向客户端发送字符串。四、实验原理
使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,它分为服务器端和客户端两部分,其主要实现过程如下1、socket函数
在网络编程中所需要进行的第一件事情就是创建一个socket,无论是客户端还是服务器端,都需要创建一个socket,该函数返回socket文件描述符,类似于文件描述符。socket是一个结构体,被创建在内核中。
sockfd=socket(AF_INET,SOCK_STREAM,0); //AF_INT:ipv4, SOCK_STREAM:tcp协议
2、connect函数
客户端创建了socket后,需要和服务器端建立连接,此时使用connect函数和服务器端进行连接。
connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))
三次握手:
第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
3、bind函数
把一个本地协议地址和套接口绑定,比如把本机的2222端口绑定到套接口。注意:为什么在上图中客户端不需要调用bind函数?这是因为如果没有调用bind函数绑定一个端口的话,当调用connect函数时,内核会为该套接口临时选定一个端口,因此可以不用绑定。而服务器之所以需要绑定的原因就是,所以客户端都需要知道服务器使用的哪个端口,所以需要提前绑定。
bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))
4、listen函数
当socket创建后,它通常被默认为是主动套接口,也就是说是默认为要马上调用connect函数的,而作为服务器是需要被动接受的,所以需要调用linsten函数将主动套接口转换成被动套接口。调用linsten函数后,内核将从该套接口接收连接请求。
/** * 3:调用listen函数监听(指定port监听) * 通知操作系统区接受来自客户端链接请求 * 第二个参数:指定队列长度 */ if(listen(sockfd,10) < 0) { perror("listen error"); }
5、accept函数
此函数返回已经握手完成的连接的套接口。注意:此处的套接口不同于服务器开始创建的监听套接口,此套接口是已经完成连接的套接口,监听套接口只是用来监听。
accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
6、write函数
c8c8
调用IO函数(read/write)和连接的客户端进行双向通信。
long t = time(0); char *s = ctime(&t); size_t size = strlen(s) * sizeof(char); //将服务器的系统时间写到客户端 if(write(fd,s,size) != size) { perror("write error"); }
7、close函数
数据传输完成后,需要关闭套接口
//关闭socket close(fd);
linux网络编程之用一张图片说明套接口常用函数
五、实验流程
服务器端代码#include "iostream" #include "netdb.h" #include "stdio.h" #include "stdlib.h" #include "sys/socket.h" #include "unistd.h" #include "arpa/inet.h" #include "string.h" #include "memory.h" #include "signal.h" #include "time.h" using namespace std; int sockfd; void sig_handler(int signo) { if(signo == SIGINT) { cout<<"Server close"<<endl; close(sockfd); exit(1); } } //输出链接上来的客户端相关信息 void out_addr(struct sockaddr_in *clientaddr) { //将断口从网络字节序转成主机字节序 int port = ntohs(clientaddr->sin_port); char ip[16]; memset(ip,0,sizeof(ip)); inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip)); cout<<"client:"<<ip<<"("<< port <<")connected\n"<<endl; } void do_service(int fd) { //获取系统时间 long t = time(0); char *s = ctime(&t); size_t size = strlen(s) * sizeof(char); //将服务器的系统时间写到客户端 if(write(fd,s,size) != size) { perror("write error"); } } int main(int argc,char *argv[]) { if(argc<2) { cout<<"usage:"<<argv[0]<<"#port"<<endl; exit(1); } if(signal(SIGINT,sig_handler) == SIG_ERR) { perror("signal sigint error"); exit(1); } /** * 1.创建socket * AF_INET:ipv4 * SOCK_STRAM:tcp协议 */ sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0) { perror("socket error"); exit(1); } //2.调用bind函数绑定socket和地址 struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr)); //往地址中填入ip,port,internet类型 serveraddr.sin_family = AF_INET; //ipv4 serveraddr.sin_port = htons(atoi(argv[1])); //htons主机字节序转成网络字节序 serveraddr.sin_addr.s_addr = INADDR_ANY; if(bind(sockfd,(struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) { perror("bind error"); exit(1); } /** * 3:调用listen函数监听(指定port监听) * 通知操作系统区接受来自客户端链接请求 * 第二个参数:指定队列长度 */ if(listen(sockfd,10) < 0) { perror("listen error"); } /** * 4:调用accept函数从队列中 * 获得一个客户端的请求链接 */ struct sockaddr_in clientaddr; socklen_t clientaddr_len = sizeof(clientaddr); while(1) { int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len); if(fd < 0) { perror("accept error"); continue; } /** * 5:调用IO函数(read/write)和 * 连接的客户端进行双向通信 */ out_addr(&clientaddr); do_service(fd); //关闭socket close(fd); } return 0; }
客户端代码
#include "netdb.h" #include "sys/socket.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "memory.h" #include "unistd.h" #include <arpa/inet.h> #include <iostream> using namespace std; int main(int argc, char *argv[]) { if(argc < 3) { cout<< "usage:"<<argv[0]<<" ip port"<<endl; exit(1); } //步骤1:创建socket int sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0) { perror("socket error"); exit(1); } struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(atoi(argv[2])); //主机字节序转换成网络字节序 inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr); //步骤2:客户端调用connect函数连接到服务器 if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) < 0) { perror("connect error"); exit(1); } //步骤3:调用IO函数(read/write)和服务器端双向通信 char buffer[1024]; memset(buffer,0,sizeof(buffer)); size_t size; if((size=read(sockfd,buffer,sizeof(buffer)))< 0) { perror("read error"); } if(write(STDOUT_FILENO,buffer,size)!=size) { perror("write error"); } return 0; }
实验结果
思考
在进行网络通信时是否需要进行字节序转换:相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。参考文献
基于TCP的客户端、服务器端socket编程Socket_编程_参考
linux网络编程之用socket实现简单客户端和服务端的通信(基于TCP)
相关文章推荐
- 基于C#的socket编程的TCP异步实现 ,包含服务器端与客户端源代码
- 客户端服务器基于Socket的UDP和TCP编程介绍
- (第三季)601-socket编程-tcp服务器端;602-socket编程-tcp客户端
- socket 网络编程快速入门(一)教你编写基于UDP/TCP的服务(客户端)通信
- 模拟客户端登陆(基于TCP的Socket编程)
- socket编程(2)-----基于UDP的客户端和服务器端的实现
- Android Socket编程基于TCP实现客户端与服务器简易通信
- 【socket编程】 一个简单的基于TCP连接的客户端、服务端用例
- 牛客网Java刷题知识点之TCP、UDP、TCP和UDP的区别、socket、TCP编程的客户端一般步骤、TCP编程的服务器端一般步骤、UDP编程的客户端一般步骤、UDP编程的服务器端一般步骤
- 基于TCP的服务器端/客户端(一)---------网络编程(linux----C)
- 基于TCP的服务器端/客户端(二)---------网络编程(Linux----C)
- 基于TCP/IP的套接字服务器端和客户端编程
- Linux Socket编程:基于TCP/IP的客户端与服务端通讯实例
- Linux socket编程入门及客户端服务器端通信实现 – 提高篇:TCP连接过程分析
- 关于Python基于TCP的服务器端和客户端编程
- 猎豹MFC--Socket编程基础TCP服务端和客户端
- VC++中Socket编程的实现-TCP服务器端
- java基于TCP的socket编程简单实现[代码实践过]
- socket编程基于tcp
- TCP编程1:客户端和服务器端的创建