服务器
- 调用socket函数创建socket;
- 调用bind函数将socket绑定到某个IP和端口的二元组上;
- 调用listen函数开始监听
- 当有客户端请求连接上来时,调用accept函数接收连接,产生一个新的socket(客户端socket);
- 基于新产生的socket调用send或者recv函数,开始与客户端进行数据交流;
- 通信结束后,调用close函数关闭监听socket。
简单的服务器代码:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main(int argc, const char * argv[]) {
// insert code here...
//std::cout << "Hello, World!\n";
//创建一个监听socket
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd == -1){
std::cout<<"create listen socket error"<<std::endl;
return -1;
}
//初始化服务器地址
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if(bind(listenfd, (struct sockaddr*) &bindaddr, sizeof(bindaddr))){
std::cout<<"bind listen socket error"<<std::endl;
return -1;
}
//启动监听
if(listen(listenfd, SOMAXCONN) == -1){
std::cout<<"listen error"<<std::endl;
return -1;
}
while(true){
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
//接收客户端连接
int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
if(clientfd != -1){
char recvBuf[32] = {0};
//从客户端接收数据
int ret = recv(clientfd, recvBuf, 32, 0);
if(ret > 0){
std::cout<<"recv data from client, data:"<<std::endl;
//将收到的数据原封不动的发给客户端
ret = send(clientfd, recvBuf, strlen(recvBuf), 0);
if(ret != strlen(recvBuf)){
std::cout<<"send data error"<<std::endl;
}else{
std::cout<<"send data to client success"<<std::endl;
}
}else{
std::cout<<"recv data error."<<std::endl;
}
close(clientfd);
}
}
//关闭监听socket
close(listenfd);
return 0;
}
客户端
- 调用socket函数创建客户端socket;
- 调用connect函数尝试连接服务器;
- 连接成功后调用send或recv函数,开始与服务器进行数据交流;
- 通信结束后,调用close函数关闭监听socket。
简单的客户端代码:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 3000
#define SEND_DATA "hello world"
int main(int argc, const char * argv[]) {
// insert code here...
//std::cout << "Hello, World!\n";
//创建一个socket
int clientfd = socket(AF_INET, SOCK_STREAM, 0);
if(clientfd == -1){
std::cout<<"create client socket error"<<std::endl;
return -1;
}
//连接服务器
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
serveraddr.sin_port = htons(SERVER_PORT);
if(connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -1){
std::cout<<"connect socket error"<<std::endl;
return -1;
}
//向服务器发送消息
int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);
if(ret != strlen(SEND_DATA)){
std::cout<<"send data error"<<std::endl;
return -1;
}
std::cout<<"send data successfully, data:"<<SEND_DATA<<std::endl;
//从服务器收取数据
char recvBuf[32] = {0};
ret = recv(clientfd, recvBuf, 32, 0);
if(ret < 0){
std::cout<<"recv data successfully, data:"<<recvBuf<<std::endl;
}else{
std::cout<<"recv data error, data:"<<recvBuf<<std::endl;
}
//关闭socket
close(clientfd);
return 0;
}
bind函数分析
函数原型:
int bind(int, const struct sockaddr *, socklen_t)
结构体sockaddr:
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
其中typedef __uint8_t sa_family_t;
结构体sockaddr_in:
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
bind函数的用法:
由bind的函数原型可以知道,第一个参数为socket文件描述符,socket变量Linux系统中为int类型;第二个参数为sockaddr类型的指针,使用sockaddr_in时需要强制类型转换;第三个参数为sockaddr_in的长度
具体实例如服务器中所示,代码片段为:
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if(bind(listenfd, (struct sockaddr*) &bindaddr, sizeof(bindaddr)) == -1){
std::cout<<"bind error"<<std::endl;
return -1;
}
如果应用程序不关心bind绑定的IP,可以使用INADDR_ANY,底层服务会自动选择一个合适的IP地址。
在TCP通信双方中,一般服务器端的端口号是固定的,而客户端的端口号是连接发起时由操作系统随机分配的。端口号是一个C short类型的值,其范围是0~65535。如果将bind函数中的端口号设置为0,那么操作系统会随机为程序分配一个可用的监听端口。但是,服务器一般不会这么做,因为必须要让客户端知道明确的IP地址和端口号。
版权声明:本文为qq_35696023原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。