首先要了解通讯的两端:数据提供者(服务器) 数据获取者(客户端) C/S模型
int listenfd socket(); //提供文件描述符 socket编程
bind(); //将IP地址和端口号绑定到文件描述符上 //Linux一切皆文件,可以把文件绑定到文件描述符上
如果绑定失败,有以下两个原因:
1.IP地址不对 2.端口号被占用或是没权限使用
listen(); //内核启动监听,收容客户端链接(面向链接,不会阻塞)
int c = accept(); //获取一个连接完成的客户端 //c是服务器连接上的客户端数
服务于c:
接收数据recv();
发送数据send();
clsoe c();
close(listenfd); //文件描述符最好手动关闭
代码:ser.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int listenfd = -1;
void sigfun(int sign)
{
close(listenfd);
exit(0);
}
int main()
{
signal(SIGINT, sigfun);
// 协议簇 TCP协议
listenfd = socket(AF_INET, SOCK_STREAM, 0);
assert(listenfd != -1);
struct sockaddr_in ser, cli;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET; // 地址簇
ser.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址
ser.sin_port = htons(6000);
int res = bind(listenfd, (struct sockaddr *)&ser, sizeof(ser));
assert(res != -1); // 绑定失败: 1、IP地址不对 2、端口号被占用或者没权限使用
listen(listenfd, 5); // size = 5 内核维护的已完成链接客户端的文件描述符个数(6)
// 服务器接受不同客户端链接的循环
while(1)
{
// 记录客户端的地址信息
int clilen = sizeof(cli);
int c = accept(listenfd, (struct sockaddr*)&cli, &clilen);
assert(c != -1);
// 与一个客户端交互的循环
while(1)
{
char buff[128] = {0};
int n = recv(c, buff, 127, 0); // 阻塞运行
if(n <= 0)
{
printf("one client unlink\n");
close(c);
break;
}
printf("%d: %s\n", c, buff);
send(c, "OK", 2, 0);
}
}
}
服务器:被动链接方 客户端:主动链接方
int socketfd = socket();
bind(); //可选 如果没有绑定,内核也会自动选择一个可用端口,自动探测本地的IP地址
connect(); //发起链接,与服务器完成链接
send();
recv();
close();
代码:cli.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
//服务器的IP地址 端口号
struct sockaddr_in ser;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(6000); // 服务器上对应服务进程的端口号
ser.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器的IP地址
int res = connect(sockfd, (struct sockaddr*)&ser, sizeof(ser));
assert(res != -1);
while(1)
{
printf("please input: ");
char buff[128] = {0};
fgets(buff, 128, stdin);
if(strncmp(buff, "end", 3) == 0)
{
close(sockfd);
break;
}
send(sockfd, buff, strlen(buff)-1, 0);
char recvbuff[128] = {0};
int n = recv(sockfd, recvbuff, 127, 0);
if(n <= 0)
{
close(sockfd);
break;
}
printf("client recv data: %s\n", recvbuff);
}
}
ser.c
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser,cli;
memset(&ser, 0 ,sizeof(0));
ser.sin_family = AF_INET;
ser.sin_port = htons(6500);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr *)&ser,sizeof(ser));
assert(res != -1);
while(1)
{
char buff[128] = {0};
int cli_len = sizeof(cli);
int n = recvfrom(sockfd,buff,127,0,(struct sockaddr*)&cli,&cli_len);
if(n <= -1)
{
close(sockfd);
break;
}
unsigned short cli_port = ntohs(cli.sin_port);
char *cli_ip = inet_ntoa(cli.sin_addr);
printf("%s:%u %s\n",cli_ip,cli_port, buff);
sendto(sockfd, "OK", 2,0,(struct sockaddr*)&cli,sizeof(cli));
}
}
cli.c
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser;
memset(&ser, 0 ,sizeof(0));
ser.sin_family = AF_INET;
ser.sin_port = htons(6500);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
while(1)
{
printf("Please input:");
char buff[128] = {0};
fgets(buff, 128, stdin);
if(strncmp(buff,"end",3) == 0)
{
close(sockfd);
break;
}
sendto(sockfd,buff,strlen(buff),0, (struct sockaddr*)&ser,sizeof(ser));
char recvbuff[128] = {0};
int n = recvfrom(sockfd,recvbuff,127,0,NULL,NULL);
if(n == 0)
{
close(sockfd);
break;
}
printf("%s \n",recvbuff);
}
}
字节流服务:
1.发送方send的次数和接收方recv的次数没有必然联系
2.如果发送方发一次数据,接收方一次没有接受完,数据不会丢失
数据报服务:
1.sendto次数与recvfrom次数相同
2.如果recvfrom一次未将数据读取完,则剩余的会丢弃