标签:tcp socket
一、套接字(socket)
套接字socket: ip地址 + port端口号。在TCP/IP协议中,它唯一标识网络通讯中的一个进程。
在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socketpair就唯一标识一个连接。
socket本身有“插座”的意思,因此用来描述网络连接的 一对一关系。
TCP/IP协议规定,网络数据流应采用 大端字节序,即 (内存)低地址高字节(数据)。
二、TCP_SOCKET 相关
TCP 协议 ---- 基于 字节流 --- SOCK_STREAM
IPv4地址格式定义在netinet/in.h中,IPv4地址: sockaddr_in结构体,包括16位端口号和32位IP地址
h表示host,n表示network,l表示32位长整数,s表示16位短整数。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol); //创建套接字
//domain: 底层通信所使用的协议 AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX
//type: 协议实现的方式 SOCK_STREAM 有序的、可靠的、双向的和基于连接的字节流 //protocol: 套接口所用的协议。前面两个参数设定后,这儿可用0指定,表示缺省。
返回值:成功 new socket的描述符,失败 -1
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//绑定
int listen(int sockfd, int backlog); //监听
// sockfd:已经建立的socket编号(描述符) SOCK_STREAM SOCK_DGRAM
// backlog: 连接请求队列的最大长度
返回值:成功 0,失败 -1
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//阻塞等待
返回值:成功 非负整数的accept socket的描述符,失败 -1
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
返回值:连接/绑定成功 0,失败 -1
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
三、TCP协议 通讯流程
1、服务器:调用socket()、bind()、listen() 完成初始化后,调用accept()阻塞等待,处于监听端口的状态。
客户端:调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。
2、数据传输的过程
建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主 动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立刻调read(),socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送 请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。
四、TCP socket API实例
//tcp_server.cpp
#include<iostream>
using namespace std;
#include<string.h>
#include<stdlib.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
const int g_backlog=5;
void usage(string _proc)
{
cout<<"Usage:"<<_proc<<"[ip][port]"<<endl;
}
static int startup(const string &ip,const int &port) //服务器初始化
{
int sock=socket(AF_INET,SOCK_STREAM,0);//创建套接字
if(sock < 0)
{
cout<<strerror(errno)<<endl;
exit(1);
}
struct sockaddr_in local;//填充本地信息
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip.c_str());
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)//绑定
{
cout<<strerror(errno)<<endl;
exit(2);
}
if(listen(sock,g_backlog) < 0)//监听
{
cout<<strerror(errno)<<endl;
exit(3);
}
return sock;
}
void *thread_run(void *arg)
{
char buf[1024];
int sock=(int)arg;
while(1)
{
memset(buf,‘\0‘,sizeof(buf));
ssize_t _size = read(sock,buf,sizeof(buf)-1);
if(_size > 0)//read success
{
buf[_size] = ‘\0‘;
}
else if(_size == 0)//close connection
{
cout<<"client close..."<<endl;
break;
}
else
{
cout<<strerror(errno)<<endl;
}
cout<<"Client# "<<buf<<endl;
}
close(sock);
return NULL;
}
int main(int argc,char *argv[])
{
if(argc != 3)
{
usage(argv[0]);
exit(1);
}
string ip = argv[1];
int port = atoi(argv[2]);
int listen_sock=startup(ip,port);
struct sockaddr_in client;
socklen_t len=sizeof(client);
client.sin_family = AF_INET;
client.sin_port = htons(port);
client.sin_addr.s_addr = inet_addr(ip.c_str());
while(1)
{
int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);//阻塞等待
if(new_sock < 0)
{
cout<<strerror(errno)<<endl;
continue;
}
cout<<"Get a connect..."<<"sock: "<<new_sock<<"ip: "<<inet_ntoa(client.sin_addr) <<"port: "<<ntohs(client.sin_port);
}
#ifdef _V1_
char buf[1024];
while(1)
{
ssize_t _size = read(new_sock,buf,sizeof(buf)-1);
if(_size > 0)//read success
{
buf[_size] = ‘\0‘;
}
else if(_size == 0)//client close
{}
else
{
cout<<strerror(errno)<<endl;
}
cout<<"Client# "<<buf<<endl;
}
#elif _V2_
cout<<"V2"<<endl;
pid_t id=fork();
if(id < 0)
{
cout<<strerror(errno)<<endl;
exit(1);
}
else if(id == 0)//child
{
string _client=inet_ntoa(client.sin_addr);
close(listen_sock);
char buf[1024];
while(1)
{
memset(buf,‘\0‘,sizeof(buf));
ssize_t _size = read(new_sock,buf,sizeof(buf)-1);
if(_size > 0)//read success
{
buf[_size] = ‘\0‘;
}
else if(_size == 0)//client close
{
cout<<_client<<"close..."<<endl;
break;
}
else
{
cout<<strerror(errno)<<endl;
}
cout<<_client<<"# "<<buf<<endl;
}
close(new_sock);
exit(0);
}
else //father
{
close(new_sock);
}
#elif _V3_
cout<<"V3"<<endl;
pthread_t tid;
pthread_create(&tid,NULL,thread_run,(void*)argc);
pthread_detach(tid);
#else
cout<<"defauult"<<endl;
#endif
return 0;
}//tcp_client.cpp
#include<iostream>
using namespace std;
#include<string.h>
#include<stdlib.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void usage(string _proc)
{
cout<<_proc<<"[remote ip][remote port]"<<endl;
}
int main(int argc,char *argv[]) //tcp_client.cpp
{
if(argc != 3)
{
usage(argv[0]);
exit(1);
}
string r_ip = argv[1];
int r_port = atoi(argv[2]);
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
cout<<strerror(errno)<<endl;
exit(1);
}
struct sockaddr_in remote;
remote.sin_family = AF_INET;
remote.sin_port = htons(r_port);
remote.sin_addr.s_addr = inet_addr(r_ip.c_str());
int ret = connect(sock,(struct sockaddr*)&remote,sizeof(remote));
if(ret < 0)
{
cout<<strerror(errno)<<endl;
}
string msg;
while(1)
{
cout<<"Please Enter: ";
cin>>msg;
write(sock,msg.c_str(),msg.size());
}
return 0;
}//Makefile
.PHONY:all all:tcp_server tcp_client tcp_server:tcp_server.cpp g++ -o $@ $^ -lpthread -D_V3_ tcp_client:tcp_client.cpp g++ -o $@ $^ .PHONY:clean clean: rm -f tcp_server tcp_client
本文出自 “花开彼岸” 博客,请务必保留此出处http://zxtong.blog.51cto.com/10697148/1775963
标签:tcp socket
原文地址:http://zxtong.blog.51cto.com/10697148/1775963