- 基础知识
(1)socket
a. 什么是socket?
socket这个词可以表示很多概念,这儿我们讲的socket是:“IP地址+端口号(TCP或UDP端口号)”。在TCP/IP协议中,它唯一标识网络通讯中的一个进程。
b. socket有什么用?
在TCP协议中,建立连接的两个进程各自有一个socket来标识,这两个socket组成 的socket pair就唯一标识一个连接。socket本身有“插座”的意思,因此用来描述网络连接的一 对一关系。
c.什么叫socketAPI
为TCP/IP协议设计的应用层编程接口称为socketAPI。
2. 程序实现图
服务器:调用socket()、bind()、listen() 完成初始化后,调用accept()阻塞等待,处于监听端口的状
态,
客户端:调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后 从accept()返回。
3. 相关函数
(1)创建套接字——socket()
a. 参数
domain:表示底层通信所使用的协议,有很多选项。这儿我们选择AF_INET格式,IPv4网络协议;
type:表示协议实现的方式,也有很多选项。这儿我们用SOCK_STREAM,它 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。 (在UDP中,我们使用参数SOCK_DGRAM ,它支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务);
protocol:套接口所用的协议。前面两个参数设定后,这儿可用0指定,表示缺省。
b. 返回值
成功返回新创建socket的文件描述符,失败返回-1
(2)绑定端口——bind()
a. 参数
sockfd:表示一个已经建立的socket编号(描述符);
addr:指向sockaddr结构体类型的指针;
addrlen:addr结构的长度,可以用sizeof函数获得。
b. 返回值
成功返回0,失败返回-1。
(3)监听客户请求——listen()
a. 参数
sockfd:表示一个已经建立的socket编号(描述符);
backlog:连接请求队列的最大长度;
b. 返回值
成功返回0,失败返回-1。
(4)接受客户端连接——accept()
返回值: 成功返回接受的socket文件描述符,失败返回-1。
(5)连接服务器——connect()
返回值:成功返回0,失败返回-1。
4. 数据传输的过程
建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主 动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立刻调 用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送 请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞 等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
如果客户端没有更多的请求了,就调用close() 关闭连接,就像写端关闭的管道一样,服务器 的read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close() 后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown() 则连接处 于半关闭状态,仍可接收对方发来的数据。
5. 代码实现
//tcp_server.cpp 1 #include<iostream> 2 #include<string> 3 #include<string.h> 4 #include<stdlib.h> 5 #include<sys/socket.h> 6 #include<sys/types.h> 7 #include<netinet/in.h> 8 #include<arpa/inet.h> 9 #include<errno.h> 10 #include<pthread.h> 11 12 using namespace std; 13 const int g_backlog=5; 14 15 void usage(string _proc) 16 { 17 cout<<"Usage:"<<_proc<<"[ip][port]"<<endl; 18 } 19 static int startup(const string &ip,const int &port) 20 { 21 //1. 22 int sock=socket(AF_INET,SOCK_STREAM,0); 23 if(sock<0) 24 { 25 cerr<<strerror(errno)<<endl; 26 exit(1); 27 } 28 //2. 29 struct sockaddr_in local; 30 local.sin_family=AF_INET; 31 local.sin_port=htons(port); 32 local.sin_addr.s_addr=inet_addr(ip.c_str()); 33 //3. 34 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) 35 { 36 cerr<<strerror(errno)<<endl; 37 exit(2); 38 } 39 //4. 40 if(listen(sock,g_backlog)<0) 41 { 42 cerr<<strerror(errno)<<endl; 43 exit(3); 44 } 45 return sock; 46 } 47 void *thread_run(void *arg) 48 { 49 int sock=(int)arg; 50 char buf[1024]; 51 while(1) 52 { 53 memset(buf,‘\0‘,sizeof(buf)); 54 ssize_t _size=read(sock,buf,sizeof(buf)-1); 55 if(_size>0) 56 { 57 //read success 58 buf[_size]=‘\0‘; 59 } 60 else if(_size==0) 61 { 62 //client close 63 cout<<"client close..."<<endl; 64 break; 65 } 66 else 67 { 68 cout<<strerror(errno)<<endl; 69 } 70 cout<<"client# "<<buf<<endl; 71 } 72 //close(sock); 73 return NULL; 74 } 75 76 int main(int argc,char* argv[]) 77 { 78 if(argc!=3) 79 { 80 usage(argv[0]); 81 exit(1); 82 } 83 string ip=argv[1]; 84 int port=atoi(argv[2]); 85 int listen_sock=startup(ip,port); 86 87 struct sockaddr_in client; 88 socklen_t len = sizeof(client); 89 while(1) 90 { 91 int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len); 92 if(new_sock<0) 93 { 94 cerr<<strerror(errno)<<endl; 95 continue; 96 } 97 cout<<"get a connect..."<<"sock:"<<new_sock 98 <<"ip:"<<inet_ntoa(client.sin_addr)<<"port:" 99 <<ntohs(client.sin_port)<<endl; 100 #ifdef _v1_ 101 //version 1 102 char buf[1024]; 103 while(1) 104 { 105 ssize_t _size=read(new_sock,buf,sizeof(buf)-1); 106 if(_size>0) 107 { 108 //read success 109 buf[_size]=‘\0‘; 110 } 111 else if(_size==0) 112 { 113 //client close 114 } 115 else 116 { 117 cout<<strerror(errno)<<endl; 118 } 119 cout<<"client# "<<buf<<endl; 120 } 121 #elif _v2_ 122 cout<<"v2"<<endl; 123 pid_t id =fork(); 124 if(id==0) 125 { 126 //child 127 std::string _client=inet_ntoa(client.sin_addr); 128 close(listen_sock); 129 char buf[1024]; 130 while(1) 131 { 132 memset(buf,‘\0‘,sizeof(buf)); 133 ssize_t _size=read(new_sock,buf,sizeof(buf)-1); 134 if(_size>0) 135 { 136 //read success 137 buf[size]=‘\0‘; 138 } 139 else if(_size==0) 140 { 141 //client close 142 cout<<_client<<"close..."<<endl; 143 break; 144 } 145 else 146 { 147 cout<<strerror(errno)<<endl; 148 } 149 cout<<_client<<"# "<<buf<<endl; 150 } 151 close(new_sock); 152 exit(0); 153 } 154 else if(id>0) 155 { 156 close(new_sock); 157 } 158 else 159 {} 160 #elif _v3_ 161 pthread_t tid; 162 pthread_create(&tid,NULL,thread_run,(void*)new_sock); 163 pthread_detach(tid); 164 #else 165 cout<<"default"<<endl; 166 #endif 167 } 168 return 0; 169 } //tcp_client.cpp 1 #include<iostream> 2 #include<string> 3 #include<string.h> 4 #include<unistd.h> 5 #include<stdlib.h> 6 #include<sys/socket.h> 7 #include<sys/types.h> 8 #include<netinet/in.h> 9 #include<arpa/inet.h> 10 #include<errno.h> 11 12 using namespace std; 13 void usage(string _proc) 14 { 15 cout<<_proc<<"[remote ip] [remote port]"<<endl; 16 } 17 int main(int argc,char* argv[]) 18 { 19 if(argc!=3) 20 { 21 usage(argv[0]); 22 exit(1); 23 } 24 25 int r_port=atoi(argv[2]); 26 string r_ip=argv[1]; 27 28 int sock=socket(AF_INET,SOCK_STREAM,0); 29 if(sock<-1) 30 { 31 cout<<strerror(errno)<<endl; 32 exit(1); 33 } 34 35 struct sockaddr_in remote; 36 remote.sin_family=AF_INET; 37 remote.sin_port=htons(r_port); 38 remote.sin_addr.s_addr=inet_addr(r_ip.c_str()); 39 40 int ret = connect(sock,(struct sockaddr*)&remote,sizeof(remote)); 41 if(ret<0) 42 { 43 cout <<strerror(errno)<<endl; 44 } 45 string msg; 46 while(1) 47 { 48 cout<<"please Enter:"; 49 cin>>msg; 50 write(sock,msg.c_str(),msg.size()); 51 } 52 return 0; 53 } //makefile 1 .PHONY:all 2 all:tcp_client tcp_server 3 tcp_client:tcp_client.cpp 4 g++ -o [email protected] $^ 5 tcp_server:tcp_server.cpp 6 g++ -o [email protected] $^ -lpthread -D_v3_ 7 .PHONY:clean 8 clean: 9 rm -f tcp_client tcp_server //statr.sh 1 #!/bin/bash 2 3 service iptables stop 4 ./tcp_server 192.168.163.128 8080