文章目录
基本流程
服务器:
-
- 创建套接字socket()
-
- 填充服务器网络信息结构体(上节记录了)
-
- 将套接字与服务器网络信息结构体绑定bind()
-
- 将服务器设置为监听状态listen()
-
- 阻塞等待客户端的连接accept()
-
- read()/write(), recv()/send(), recvfrom()/sendto()等函数通信
客户端:
- read()/write(), recv()/send(), recvfrom()/sendto()等函数通信
-
- 创建套接字socket()
-
- 填充网络服务器信息结构体
-
- connect()给服务器发送请求
-
- 进行通信
-
- close() 关闭
相关函数
socket()函数
#include <sys/socket.h>
int socket(int family, int type, int protocol);
创建一个套接字,返回文件描述符,失败返回-1.
family
指明协议族可以为以下几种:
AF_INET
:IPv4协议AF_INET6
:IPv6协议AF_LOCAL
:Unix域协议AF_ROUTE
:路由套接字AF_KEY
:秘钥套接字
type
指明套接字的类型,可以是
SOCKET_STREAM
:字节流套接字SOCKET_DGRAM
:数据报套接字–UDPSOCKET_SEQPACKET
:有序分组套接字SOCKET_RAW
:原始套接字
protocol
:附加协议,传0表示不需要其他协议,一些协议有
IPPROTO_TCP
: TCP传输协议IPPROTO_UDP
: UDP传输协议IPPROTO_SCTP
: SCTP传输协议
connect()函数
函数原型
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
功能是TCP客户端建立与TCP服务器的连接
sockfd
是socket()函数返回的套接字描述符
servaddr
是包含要连接服务器网络信息的结构体
addrlen
servaddr的大小
在调用connect
前不必非得调用bind()
函数,因为操作系统内核会确定源IP地址和临时端口号
该函数建立成功后会返回0, 失败返回-1。
bind()函数
函数原型
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddy, socklen_t addrlen);
功能是将客户端套接字与网络信息结构体绑定
sockfd
同上,是socket()
函数返回的套接字描述符
myaddr
是用户网络信息结构体指针
addrlen
网路信息结构体的大小
listen()函数
函数原型
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockte()
函数创建套接字时,被设定为一个主动套接字,是一个将调用connect()
函数发起连接请求的客户套接字。listen
把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。会使一个未连接的套接字由CLOSED状态转换成LISTEN状态。
参数:
sockfd
:socked文件描述符
backlog
:允许同时连接的客户端的个数,和内核为监听套接字维护的两个队列有关,两个队列如下:
未完成连接队列:服务器在三次握手中受到第一个握手信号后进入SYN——RCVD状态,为这样的套接字维护的队列
已完成连接队列:完成三次握手的套接字所维护的队列。
accept()函数
函数原型
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
服务器调用该函数,用于从已完成连接队列队头返回下一个已完成连接,如果已完成连接队列为空,则进程进入睡眠状态。
参数
sockfd
:同上
cliaddr
:用来返回已连接的对端(客户)的协议地址。
addrlen
:addr的大小
服务器调用该函数后,可能会返回错误值或一个全新的套接字描述符,参数中的第一个参数一般称为监听套接字,调用函数后,返回一个新的套接字称为已连接套接字。系统中常常仅创建一个监听套接字进程,而当三次握手完成后,系统会为每个连接建立一个已连接套接字。
函数使用尝试
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
int sockfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t addrlen = sizeof(serveraddr);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("socket error\n");
exit(1);
}
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
if(bind(sockfd, (struct sockaddr*)&serveraddr, addrlen) == -1)
{
printf("bind error\n");
exit(1);
}
if(listen(sockfd, 5) == -1)
{
printf("listen error\n");
exit(1);
}
if(accept(sockfd, (struct sockaddr*)&clientaddr, &addrlen) == -1)
{
printf("accept error");
exit(1);
}
printf("client %s : %d connected\n",inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
int sockfd;
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
connect(sockfd, (struct sockaddr*)&serveraddr, addrlen);
return 0;
}
客户端服务器通信相关函数
recv()/send()函数
函数原型
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
实现接收数据,返回成功接收的字节数,失败则为-1。
参数
sockfd
:文件描述符,在客户端是socket()
函数的返回值,在服务器是accept()
的返回值
buf
:保存接收到的数据
len
:理论要接收的字节数
flags
:标志位,0阻塞,MSG_DONTWAIT非阻塞
函数原型
#include <sys/socket.h>
ssize_t send(int sockfd, const void* buf, size_t len, int falgs)
实现发送数据,返回成功发送的字节,失败返回-1。
参数
sockfd
:文件描述符,在客户端是socket()
函数的返回值,在服务器是accept()
的返回值
buf
:要发送的数据
len
:理论要发送的字节数
flags
:标志位,0阻塞,MSG_DONTWAIT非阻塞
recv()和send()函数使用
服务器
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <cstring>
using namespace std;
int main(int argc, char *argv[])
{
if(argc < 3)
{
printf("please input <ip> <port>\n");
exit(1);
}
int sockfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t addrlen = sizeof(serveraddr);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
bind(sockfd, (struct sockaddr*)&serveraddr, addrlen);
listen(sockfd, 5);
int acceptfd;
acceptfd = accept(sockfd,(struct sockaddr*)&clientaddr, &addrlen);
printf("client %s : %d connected\n",inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
ssize_t bytes;
char buf[100] = {0};
while(1)
{
bytes = recv(acceptfd, buf, 100, 0);
if(bytes == -1)
{
printf("recv error\n");
}
else if(bytes == 0)
{
printf("client exit\n");
acceptfd = accept(sockfd,(struct sockaddr*)&clientaddr, &addrlen);
}
if(strcmp(buf, "quit") == 0)
{
printf("client exit\n");
acceptfd = accept(sockfd,(struct sockaddr*)&clientaddr, &addrlen);
}
printf("%s-%d: %s\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), buf);
strcat(buf, "^_^");
if(send(acceptfd, buf, 100, 0) == -1)
{
printf("send error\n");
}
}
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
if(argc < 3)
{
printf("please input <ip> <port>\n");
exit(1);
}
int sockfd;
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr*)&serveraddr, addrlen);
char buf[100] = {0};
while(1)
{
fgets(buf, 100, stdin);
buf[strlen(buf) - 1] = '\0';
if(send(sockfd, buf, 100, 0) == -1)
{
printf("send error\n");
}
if(strcmp(buf, "quit") == 0)
{
printf("client exit\n");
exit(0);
}
memset(buf, 0, 100);
if(recv(sockfd, buf, 100, 0) == -1)
{
printf("recv error\n");
}
printf("server: %s\n", buf);
}
return 0;
}