socket编程(超简单、详细、可运行)--实现一个简单的聊天程序

首先我们先来看一下套接字通讯的流程!

图片中的read()和write()等同于send()和recv()
socket通信流程

服务器端:
1、创建套接字socket
2、绑定套接字bind
3、监听listen
4、等待连接accept
5、接收发送recv、send
6、关闭套接字close
客户端:
1、创建套接字socket
2、连接服务器connect
3、接收发送recv、send
4、关闭套接字close

我们先看一下这样几个函数,稍后我们在继续

#include<arpa/inet.h>
unit32_t htonl(unit32_t hostlong);//32位的长整数从主机字节序转换为网络字节序
unit16_t htons(unit16_t hostshort);//16位的短整数从主机字节序转换为网络字节序
unit32_t ntohl(unit32_t netlong);//32位的长整数从网络字节序转换为主机字节序
unit16_t ntohs(unit16_t netshort);//16位的短整数从网络字节序转换为主机字节序
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
int inet_aton(const char* cp, struct in_addr *inp);//将字符串cp的十进制转换为网络字节序的二进制形式后存储到inp中
char* inet_ntoa(struct in_addr *in);//将网络字节序的二进制形式转换为十进制的字符串形式,返回字符串的首地址
unsigned long inet_addr(const char* cp);//inet_addr()的功能是将一个点分十进制的IP转换成网络字节的32位二进制数值并返回(uint32_t)

好了这些函数,我们稍后再来讲解。
接下来我们来看一下套接字的地址结构体

//通用套接字地址结构体,不常用
struct sockaddr{
unit8_t sa_len;
sa_famliy sa_famlity;
char sa_data[14];
}
// IPV4套接字地址结构体:
struct sockaddr_in{
unit8_t sin_len;
sa_famliy_t sin_famliy;//协议家族
in_port_t sin_port;//端口号
struct in_addr  sin_addr;//IP地址
char sin_zero[8];
}
struct in_addr
{
uint32_t s_addr;//IP
}

接下来我们根据套接字通信流程来看一下相应的一些函数

int socket(int domain,int type,int protocol);
头文件:
#include <sys/types.h>
#include <sys/socket.h>1个参数domain指明了协议族/域,通常AF_INET、AF_INET6、AF_LOCAL等;
第2个参数type是套接口类型,主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;
第3个参数protocol一般取为0。成功时,返回一个小的非负整数值,与文件描述符类似
返回值:成功–非负描述符, 出错-1 

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);1个参数sockfd是用socket()函数创建的文件描述符。
第2个参数my_addr是指向一个结构为sockaddr参数的指针,sockaddr中
包含了地址、端口和IP地址的信息。在进行地址绑定的时候,需要弦将
地址结构中的IP地址、端口、类型等结构struct sockaddr中的域进行设
置之后才能进行绑定,这样进行绑定后才能将套接字文件描述符与地址等接合在一起。
第3个参数addrlen是my_addr结构的长度,可以设置成sizeof(struct
 sockaddr)。使用sizeof(struct sockaddr)来设置套接字的类型和其
 对已ing的结构。
bind()函数的返回值为0时表示绑定成功,-1表示绑定失败,errno的错误值如表1所示。

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);1个参数sockfd参数 是用socket()函数创建的文件描述符。
第2个参数backlog参数 此参数指定了sockfd句柄上pending的连接的队列可以达到的最大的长度。
返回值,成功返回0, 失败返回-1.

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)1个参数:sockfd 监听套接字,即服务器端创建的用于listen的socket描述符。  
第2个参数:addr  这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址  
第3个参数:len 描述 addr 的长度 
返回值,成功返回链接的描述符, 失败返回-1

#include <sys/types.h>
#include <sys/socket.h>
int send( SOCKET s, const char FAR *buf, int len, int flags );1个参数指定发送端套接字描述符;
第2个参数指明一个存放应用程序要发送数据的缓冲区;
第3个参数指明实际要发送的数据的字节数;
第4个参数一般置0。
返回值:成功返回发送的字符数,失败返回-1

int recv( SOCKET s, char FAR *buf, int len, int flags);1个参数指定接收端套接字描述符;
第2个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第3个参数指明buf的长度;
第4个参数一般置0。
返回值:成功返回发送的字符数,失败返回-1

close函数就不说了。

说了这莫多开始上代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 6789//1024~49050
#define IP "192.168.43.130"
int main()
{
  char r_buf[100]={0};
  char w_buf[100]={0};
  struct sockaddr_in addr,cli_addr;
  int len=sizeof(addr);
  int sockfd=socket(AF_INET,SOCK_STREAM,0);//IPV4,tcp
  if(sockfd==-1)
  {
    perror("error socket");
    return -1;
  
  }
 printf("连接套接字创建成功\n");
 addr.sin_family=AF_INET;
 addr.sin_port=htons(PORT);//转换
 addr.sin_addr.s_addr=inet_addr(IP);//转换
 int ret=bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
 if(ret==-1)
 {
	perror("bind error");
        return -1;
 }
  printf("绑定成功\n");
 ret=listen(sockfd,3);
 if(ret==0)
 {
   printf("监听成功\n");
 }
 else
 {
  return -1;
 }
 int comfd = accept(sockfd,(struct sockaddr *)&cli_addr,&len);
 if(comfd==-1)
 {
   perror("accept error");
   return -1;
 }
 printf("客户端%s已连接\n", inet_ntoa(cli_addr.sin_addr));

 while(1)
 {
  memset(r_buf,0,sizeof(r_buf));
  recv(comfd,r_buf,sizeof(r_buf),0);
  printf("服务器接收%s\n", r_buf);
  printf("服务器发送\n");
  memset(w_buf,0,sizeof(w_buf));
  scanf("%s", w_buf);
  send(comfd,w_buf,sizeof(w_buf),0);
  }
 close(sockfd);
 close(comfd);
   return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 6789
#define IP "192.168.43.130"
int main()
{
  char r_buf[100]={0};
  char w_buf[100]={0};
  struct sockaddr_in addr;
  int len=sizeof(addr);
  int sockfd=socket(AF_INET,SOCK_STREAM,0);
  if(sockfd==-1)
  {
    perror("error socket");
    return -1;
  
  }
 printf("连接套接字创建成功\n");
 addr.sin_family=AF_INET;
 addr.sin_port=htons(PORT);
 addr.sin_addr.s_addr=inet_addr(IP);


 connect(sockfd,(struct sockaddr *)&addr,len);

 while(1)
 {
  printf("客户端发送\n");
  memset(w_buf,0,sizeof(w_buf));
  scanf("%s", w_buf);
  send(sockfd,w_buf,sizeof(w_buf),0);  

  memset(r_buf,0,sizeof(r_buf));
  recv(sockfd,r_buf,sizeof(r_buf),0);
  printf("客户端接收%s\n", r_buf);

  }
 close(sockfd);
   return 0;
}

看一下测试运行结果:
在这里插入图片描述
这次实现的功能也比较简单,后续会继续跟一点socket编程部分的高阶!
感谢评阅,欢迎交流指正!
Q:918619587

  • 9
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值