以前学的是Java中的socket通信,现在由于实习原因,更多接触C++了,今天来讲讲C++中的socket通信。
socket的基本操作:
1,socket()函数
int socket(int domain, int type, int protocol);
socket()用于创建一个socket的套接字,它唯一标识一个socket。后续的操作都有用到这个套接字,把它作为参数,通过它来进行一些读写操作。
socket()的参数:
domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL...
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET等等
protocol:指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等。当protocol为0时,会自动选择type类型对应的默认协议。
使用:int server_socket = socket(PF_INET, SOCK_STREAM, 0);---返回socket套接字
2,bind()函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。
bind()的参数:
sockfd:即socket套接字,它是通过socket()函数创建了,唯一标识一个socket.
addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
addrlen:对应的是地址的长度。
使用:bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))---返回0即表示成功
3,listen()、connect()函数
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
服务器:int listen(int sockfd, int backlog);
客户端:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函数的第一个参数即为要监听的socket套接字,第二个参数为相应socket可以排队的最大连接个数。
connect函数的第一个参数即为客户端的socket套接字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。
4,accept()函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。
accept函数返回的是已连接的socket套接字。
socket中TCP建立连接的三次握手:
tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:
客户端向服务器发送一个SYN J
服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
客户端再想服务器发一个确认ACK K+1
当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
socket中TCP释放连接的四次握手:
某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;接收到这个FIN的源发送端TCP对它进行确认。这样每个方向上都有一个FIN和ACK。
TCP socket通信过程:
服务端:
1,创建socket套接字:
SOCKET sockSrv =socket(AF_INET, SOCK_STREAM,0);
2,套接字分配地址信息(端口和ip):
int retVal =bind(sockSrv,(LPSOCKADDR)&addrSrv,sizeof(SOCKADDR_IN));
3,监听socket:listen(sockSrv,10)
4,接受客户端的连接请求:
SOCKET sockConn =accept(sockSrv,(SOCKADDR *)&addrClient,&len);
客户端:
1,创建套接字:
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
2,向服务端发出连接请求:
connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv))
以上,客户端和服务端连接成功后,就可以通信了。
最后,socket通信还可以跨系统哟~
参考代码:
Linux上的TCP socket 通信:
服务端:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
客户端:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main()
{
//加载套接字
WSADATA wsaData;
char buff[1024];
memset(buff, 0, sizeof(buff));
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Failed to load Winsock");
return;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(5099);
addrSrv.sin_addr.S_un.S_addr = inet_addr("10.130.42.91");
//创建套接字
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if (SOCKET_ERROR == sockClient){
printf("Socket() error:%d", WSAGetLastError());
return;
}
//向服务器发出连接请求
if (connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET){
printf("Connect failed:%d", WSAGetLastError());
return;
}
else
{
//接收数据
recv(sockClient, buff, sizeof(buff), 0);
printf("%sn", buff);
}
//发送数据
char *buffSend = "hello, this is a Client....";
send(sockClient, buffSend, strlen(buffSend) + 1, 0);
printf("%d", strlen(buffSend) + 1);
//关闭套接字
closesocket(sockClient);
WSACleanup();
system("pause");
}
Linux上的TCP socket 通信:
服务端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int server_sockfd;//服务器端套接字
int client_sockfd;//客户端套接字
int len;
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
int sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family=AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port=htons(8000); //服务器端口号
/*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/
if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
perror("socket error");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if(bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)
{
perror("bind error");
return 1;
}
/*监听连接请求--监听队列长度为5*/
if(listen(server_sockfd,5)<0)
{
perror("listen error");
return 1;
};
sin_size=sizeof(struct sockaddr_in);
/*等待客户端连接请求到达*/
if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr,&sin_size))<0)
{
perror("accept error");
return 1;
}
printf("accept client %s/n",inet_ntoa(remote_addr.sin_addr));
len=send(client_sockfd,"Welcome to my server/n",21,0);//发送欢迎信息
/*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/
while((len=recv(client_sockfd,buf,BUFSIZ,0))>0)
{
buf[len]='0';
printf("%s/n",buf);
if(send(client_sockfd,buf,len,0)<0)
{
perror("write error");
return 1;
}
}
/*关闭套接字*/
close(client_sockfd);
close(server_sockfd);
return 0;
}
客户端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int client_sockfd;
int len;
struct sockaddr_in remote_addr; //服务器端网络地址结构体
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零
remote_addr.sin_family=AF_INET; //设置为IP通信
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址
remote_addr.sin_port=htons(8000); //服务器端口号
/*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
perror("socket error");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0)
{
perror("connect error");
return 1;
}
printf("connected to server/n");
len=recv(client_sockfd,buf,BUFSIZ,0);//接收服务器端信息
buf[len]='0';
printf("%s",buf); //打印服务器端信息
/*循环的发送接收信息并打印接收信息(可以按需发送)--recv返回接收到的字节数,send返回发送的字节数*/
while(1)
{
printf("Enter string to send:");
scanf("%s",buf);
if(!strcmp(buf,"quit")
break;
len=send(client_sockfd,buf,strlen(buf),0);
len=recv(client_sockfd,buf,BUFSIZ,0);
buf[len]='/0';
printf("received:%s/n",buf);
}
/*关闭套接字*/
close(client_sockfd);
return 0;
}