我正在参加「掘金·启航计划」。
1. socket编程的基本概念
socket就是插座,运行在计算机中的两个程序通过socket建立起一个通道,数据在通道中传输。 socket将复杂的TCP/IP协议族隐藏了起来,我们用好socket相关函数就可完成网络通信。
分为
流socket
基于TCP协议的
和
数据报socket
基于UDP协议的
两种。
2.简单的socket通信流程
程序部分要用到的头文件:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//添加了这个头文件就不需要sys/socket那个了
#include <arpa/inet.h>
step1:创建流式socket,socket()
/*创建监听的套接字,一般都是用tcp的*/
int fd = socket(AF_INET,SOCK_STREAM,0);
//判断是否创建成功,创建不成功打印错误信息
if(fd == -1){
perror("socket 创建失败");
return -1;
step2:指定用于通信的ip和端口bind()
/*将套接字与本地ip、端口进行绑定 */
struct sockaddr_in saddr; //因为这个结构体初始化简单
//初始化saddr,三个成员
//sa_family_t sin_family; 地址族协议: AF_INET
//in_port_t sin_port; 端口, 2字节-> 大端
//struct in_addr sin_addr; IP地址, 4字节 -> 大端
saddr.sin_family = AF_INET; // IPv4
saddr.sin_port = htons(9999); // 端口
saddr.sin_addr.s_addr = INADDR_ANY; //0.0.0.0;可以绑定本地任意ip地址
//绑定 绑定失败会返回-1,若绑定失败要打印错误信息
int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
if(ret == -1){
perror("socket绑定失败");
return -1;
step3:把服务器设为监听模式listen()
ret = listen(fd, 128);
if(ret == -1){
perror("监听失败");
return -1;
step4:接受客户端端连接accept()
/*阻塞并等待客户端的连接*/
struct sockaddr_in caddr; //方便取数据
int addrlen = sizeof(caddr);
int cfd = accept(fd,(struct sockaddr*)&caddr, &addrlen);
if(cfd == -1){
perror("accept失败");
return -1;
step5:接收/发送数据,recv()/send()
/*建立连接成功后,打印客户端ip和端口信息*/
//保存到caddr中的ip和端口都是大端到,我们需要先转成小端再输出
char ip[32];
printf("客户端的IP: %s, 端口: %d\n",inet_ntop(AF_INET,&caddr.sin_addr.s_addr,ip,sizeof(ip)),ntohs(caddr.sin_port));
/* 5.通信*/
while (1)
//接收数据
char buff[1024];
int len = recv(cfd,buff,sizeof(buff),0);//flags: 特殊的属性,一般不使用,指定为 0
if(len > 0){
printf("client say: %s\n", buff);
send(cfd, buff, len, 0);
}else if(len == 0){
printf("客户端已断开连接...\n");
break;
}else{
//到这里说明函数调用失败
perror("recv error");
break;
step6:关闭socket连接,释放资源close()
//关闭文件描述符
close(fd); //用于监听的
close(cfd); //用于通信的
step1:创建流式socket,socket()
这里与服务端一样,int fd = socket(AF_INET,SOCK_STREAM,0);
不再赘述了。
step2:向服务器发起连接请求,connect()
/*连接服务器的ip和port */
struct sockaddr_in saddr; //因为这个结构体初始化简单
//初始化saddr,三个成员
// sa_family_t sin_family; 地址族协议: AF_INET
// in_port_t sin_port; 端口, 2字节-> 大端
// struct in_addr sin_addr; IP地址, 4字节 -> 大端
saddr.sin_family = AF_INET; // IPv4
saddr.sin_port = htons(9999); // 端口,服务器绑定的9999端口那客户端连接的话也要连接到这个端口
//需要将本机ip地址转成一个大端地址, //客户端要指定一个ip地址了
inet_pton(AF_INET,"172.18.45.233",&saddr.sin_addr.s_addr); //将ip存到saddr
// saddr.sin_addr.s_addr = INADDR_ANY; //客户端要指定一个ip地址了
//绑定 绑定失败会返回-1,若绑定失败要打印错误信息
int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
if(ret == -1){
perror("socket连接失败");
return -1;
step3:发送/接收数据,send()/recv()
/*建立连接成功后,直接进行通信*/
/*进行套接字通信*/
int number = 0;
while (1)
/*先发数据再接数据*/
//发送数据
char buff[1024]; //数据先初始化到buff
sprintf(buff, "你好,hello, world, %d..\n",number++);
send(fd, buff, strlen(buff)+1, 0);
//接收数据
memset(buff,0,sizeof(buff));//先清空buff中的数据
int len = recv(fd, buff,sizeof(buff),0);//flags: 特殊的属性,一般不使用,指定为 0
if(len > 0){
printf("server say: %s\n", buff);
}else if(len == 0){
printf("服务器已断开连接...\n");
break;
}else{
//到这里说明函数调用失败
perror("recv error");
break;
sleep(1); //这里只是让客户端发数据发的慢一些,每1s发一次
step4:关闭socket连接,释放资源close()
//关闭文件描述符
close(fd); //用于监听的