创建通信端点:
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
第一个参数:domain参数指定通信域,这将选择将用于通信的协议系列,都被定义在<sys/socket.h>中,这里我们使用AF_INET协议,代表以ipv4方式通信
第二个参数:套接字具有指示的类型,该类型指定通信语义,这里我们使用SOCK_STREAM,意思是提供顺序的,可靠的,双向的,基于连接的字节流
第三个参数:该协议指定要与套接字一起使用的特定协议,一般情况下,在一个协议系列中只有一个协议支持一个特殊的套接子类型,所以一般设为0
返回值:成功返回文件描述符,失败返回-1
将地址绑定到套接字:
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
第一个参数:套接字描述符
第二个参数:分配给套接字描述符的地址
第三个参数:地址长度
返回值:成功返回0,失败返回-1
监听套接字上的连接:
listen将sockfd所引用的套接字标记为被动套接字,即,该套接字将用于使用accept接受传入的连接请求。
#include <sys/socket.h>
int listen(int sockfd, int backlog);
第一个参数:套接字描述符
第二个参数:backlog参数定义sockfd的未连接队列可以增长的最大长度
返回值:成功返回0,失败返回-1
接受套接字上的连接:
accept系统调用与基于连接的套接字类型(SOCK_STREAM,SOCK_SEQPACKET)一起使用。
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
第一个参数:套接字描述符
第二个参数:客户端结构体对象的指针
第三个参数:客户端结构体对象的大小
返回值:成功时,这些系统调用将返回非负整数,该整数是已接受套接字的描述符,失败返回-1
构建服务器端代码:
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
using namespace std;
int main(int argc, char* argv[]){
int serv_sock, clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
char message[] = "Hello World";
if (argc != 2) {
cout << "Usage : " << argv[0] << " <port> " << endl;
return -1;
}
serv_sock = socket(AF_INET, SOCK_STREAM, 0);
if (serv_sock == -1){
cout << "Create server socket failed ..." << endl;
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
int ret = bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (ret == -1) {
cout << "bind failed ..." << endl;
return -1;
}
ret = listen(serv_sock, 5);
if (ret == -1) {
cout << "listen failed ..." << endl;
return -1;
}
socklen_t clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -1) {
cout << "accept failed ..." << endl;
return -1;
}
write(clnt_sock, message, sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
客户端发送连接请求:
connect系统调用将文件描述符sockfd引用的套接字连接到addr指定的地址。
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
第一个参数:套接字描述符
第二个参数:sockaddr_in 结构体对象指针
第三个参数:sockaddr_in 结构体对象大小
返回值:成功返回0,失败返回-1;
构建客户端代码:
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char* argv[]) {
struct sockaddr_in serv_addr;
char message[30];
int str_len;
if (argc != 3) {
cout << "Usage : " << argv[0] << " <IP> <port>" << endl;
return -1;
}
int clnt_sock = socket(AF_INET, SOCK_STREAM, 0);
if (clnt_sock == -1){
cout << "Create client socket failed ..." << endl;
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
socklen_t serv_addr_len = sizeof(serv_addr);
int ret = connect(clnt_sock, (struct sockaddr*)&serv_addr, serv_addr_len);
if (ret == -1) {
cout << "connect failed ..." << endl;
return -1;
}
str_len = read(clnt_sock, message, sizeof(message) - 1);
if (str_len == -1) {
cout << "read failed ..." << endl;
return -1;
}
cout << "message from server: " << message << endl;
close(clnt_sock);
return 0;
}
创建套接字并不能决定那个是服务器端,那个是客户端。如果紧接着执行bind、listen函数,将成为服务器套接字,如果调用connect函数,将成为客户端套接字。
客户端通过调用connect函数向服务器发送链接请求。
服务器端调用accept函数受理连接请求,如果在没有连接的情况下调用该函数,则不会返回,直到有连接请求为止。
服务器的write函数向套接字写入消息,客户端的read函数从套接字读取信息。
执行过程:
在命令行分别将服务器端代码和客户端代码编译,生成两个可执行文件,并在两个终端分别执行可执行文件
g++ client.cpp -o client
g++ server.cpp -o server
./server 9190 # 服务器端命令
./client 127.0.0.1 9190 # 客户端命令