第一节
socket 描述符就是文件描述符,都是一个 int 整数,有着身份证的作用
1.1 socket 函数
| int socket(int domain, int type, int protocol); |
|---|
| 功能:设置 socket 通讯的 IP 域与端口通信方式后,分配一个 socket 通信的 socket 描述符 |
| 参数 1:domin(域)<==>填写 IP 域是属于 IPv4 IPv6 本地 IP AP_INET=PIv4 AF_INET6=IPv6 AF_LOCAL=本地 IP |
| 参数 2:type<==>socket 的端口通信方式选择 SOCK_STEAM=TCP 通信的流式套接字 SOCK_DGRAM=UDP 通信的数据报套接字 SOCK_RAW=原始套接字 |
| 参数 3:protocol(协议)<==>UDP 与 TCP 编程时写 0,原始套接字编程时填充 |
| 返回值:成功->socket 描述符 ; 失败->EOF |
1.2 bind 函数
| int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
|---|
| 功能: 将 socket 网络地址与 socket 描述符绑定关联 |
| 参数 1:sockfd <==> socket 获取的 socket 描述符 |
| 参数 2:sockaddr 网络地址结构体变量的地址(IPv4,IPv6,本地网络的网络地址结构体都是不一样的) ipv4 网络地址结构体如下(man 7 ip 可查看) : ![]() |
| 参数 3:socket 网络地址结构体的长度 |
| 返回值: On success, zero is returned. On error, -1 is returned, |
1.3 listen 函数
listen(倾听)
| int listen(int sockfd, int backlog); |
|---|
| 功能:l 因为服务器是被动连接,套接字默认是主动的,所以使用 isten()将 socket 主动套接字转化为被动套接字,让服务器进入监听状态 |
| 参数 1:socket 函数获取的 socket 描述符 |
| 参数 2" 最大允许几个客户端进行连接 |
| 返回值: On success, zero is returned. On error, -1 is returned |
1.4 accept 函数
| int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); |
|---|
| 功能: 一直等待客户端连接 |
| 参数 1:服务器的 socket 描述符 |
| 参数 2:客户端套接字网络地址 |
| 参数 3: 参数 2 的字节长度 |
| 返回值: On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket. On error, -1 is returned (成功:如果与客户端连接成功,就返回一个新的 newfd,用来临时通信,失败的话:-1) |
1.5 read write close 参考前面的文章
由于 socket 网络描述符就是文件描述符,所以在 accept 连接成功后,直接使用 read write close 函数即可 accept 返回的操作临时文件描述符 newfd
1.6 客户端连接函数 connet
| int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
|---|
| 功能:连接服务器 |
| 参数 1:客户端的 socket 描述符 |
| 参数 2:用于接收客户端的 socket 网络地址结构体 |
| 参数 3:用于接收参数 2 的字节长度 |
| 返回值: If the connection or binding succeeds, zero is returned. On error, -1 is returned |
服务器ser.c 客户端cli.c
实现C/S通信
/*===============================================================
* Copyright (C) 2020 All rights reserved.
*
* 文件名称:ser.c
* 创 建 者:liujing
* 创建日期:2020年07月24日
* 描 述:
*
* 更新日志:
*
================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> * superset of previous */
void main()
{
1:获取socket描述符
int socketid;
AF_INET=IPv4
SOCK_STREAM=socket流
0=协议默认
socketid = socket(AF_INET,SOCK_STREAM,0);
if(socketid!=EOF)
{
printf("\n获取ID成功\n");
}
else
{
perror("\n获取ID失败\n");
}
2:socket网络地址绑定socket描述符
socket网络地址赋值 --> For AF_INET see ip(7)
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = 5001; 5000~65535是用户段口号
将点分十进制的IP地址转化为32位地址,存储在sockaddr.sin_addr
if(1 == inet_pton(AF_INET,"192.168.43.70",(void*)&sockaddr.sin_addr))
{
printf("\n转换IP-->32位成功\n");
}
else
{
perror("\n转化IP-->位失败\n");
}
socket描述符绑定网络地址
if(bind(socketid,(struct sockaddr*)&sockaddr,sizeof(sockaddr)) == EOF)
{
perror("\nbind function error\n");
}
else
{
printf("\nbind function success\n");
}
将网络套接字转化为被动套接字,进入监听状态
if(0 == listen(socketid,5))
{
printf("\n listen function success\n");
}
else
{
perror("\nlisten function error:\n");
}
等待客户端请求连接
struct sockaddr_in cliaddr;
int clilen = 0;
int newid = -1;
printf("\n阻塞等待接入\n");
newid = accept(socketid,(struct sockaddr*)&cliaddr,&clilen);
if(newid == -1)
{
perror("\naccept function error\n");
}
else
{
printf("\naccept function success\n");
}
read write 读取操作文件描述服
char buf[32];
char sendbuf[32] = "HELLO WORD";
int ret;
while(1)
{
ret = read(newid,buf,32);
write(newid,sendbuf,32);
puts(buf);
sleep(1);
if(ret == 0) 当read write函数返回0,说明客户端退出,socket连接断开
{
printf("\nbreak\n");
break; 退出循环
}
}
close(newid);
close(socketid);
printf("\nend\n");
}
/*===============================================================
* Copyright (C) 2020 All rights reserved.
*
* 文件名称:cli.c
* 创 建 者:liujing
* 创建日期:2020年07月25日
* 描 述:
*
* 更新日志:
*
================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h> * See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> * superset of previous */
void main()
{
1.获取网络套结字描述符
int socketid;
socketid = socket(AF_INET,SOCK_STREAM,0);
AF_INET=IPv4
SOCK_STREAM=socket流
0=协议默认
if(socketid!=EOF)
{
printf("\n获取ID成功\n");
}
else
{
perror("\n获取ID失败\n");
}
2.连接服务器
socket网络地址赋值 --> For AF_INET see ip(7)
struct sockaddr_in sockaddr; 接受客户端的网络地址结构体
sockaddr.sin_family = AF_INET;//接受客户端的网络地址结构体的大小
sockaddr.sin_port = 5001; 5000~65535是用户段口号
将点分十进制的IP地址转化为32位地址,存储在sockaddr.sin_addr
if(1 == inet_pton(AF_INET,"192.168.43.70",(void*)&sockaddr.sin_addr))
{
printf("\n转换IP-->32位成功\n");
}
else
{
perror("\n转化IP-->位失败\n");
}
if(0 == connect(socketid,(struct sockaddr*)&sockaddr,sizeof(sockaddr)))
{
printf("\nconnet function success\n");
}
else
{
perror("connect funtion error");
}
3.通信
char buf[32] = "hello word";
char getbuf[32] = {0};
int ret;
while(1)
{
write(socketid,buf,32); 注意:服务器的收发的字节大小应该保持一致
ret = read(socketid,getbuf,32);
puts(getbuf);
sleep(1);
if(ret == 0)
{
printf("\n退出循环\n");
break;
}
}
close(socketid);
printf("\nend\n");
}
====================================================================
ser的运行结果截取如下:
farsight@ubuntu:~/Desktop$ gcc cli.c -o cli
farsight@ubuntu:~/Desktop$ gcc ser.c -o ser
farsight@ubuntu:~/Desktop$ ./ser
获取ID成功
转换IP-->32位成功
bind function success
listen function success
阻塞等待接入
accept function success
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
hello word
break
end
farsight@ubuntu:~/Desktop$
====================================================================
cli的运行结果截取如下:
farsight@ubuntu:~/Desktop$ ./cli
获取ID成功
转换IP-->32位成功
connet function success
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
HELLO WORD
^C
farsight@ubuntu:~/Desktop$
服务器的 IP 地址是动态的话,将网络地址结构体的 s_addr 地址设置位 INADDY_ANY 即可
文章转载自西红柿爆炒鸡蛋,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。





