在上节课(Linux网络编程初体验)中我们实现了连接bilibili的功能,并获取其html源码
如图所示.
今天我们要自己编写个服务端来服务我们的客户端
提示:以下是本篇文章正文内容,下面案例可供参考
服务端长期暴露于网络,并等待客户端连接
特点:
服务端无法主动连接客户端
客户端只能按照预定义的方式连接服务端
服务端编程模式:
socket()->bind()->listen()->accept()->client_sock->
send()/recv->close()
int bind(int sock,struct sockaddr*addr,socklen_t addrlen);//参数1:服务端sock,参数2:服务端sockaddr,参数3:参数2的大小,类型为socklen_t
int listen(int sock,int backlog);//参数1:服务端sock,参数2:直接填1
int accept(int sock,struct sockaddr*addr,socklen_t*addrlen);//参数1:客户端sock,参数2接受客户端addr的地址,参数3:参数2的长度
accept()的返回值为客户端的sock
服务端socket只用于连接,不进行通讯
服务端的socket用于产生客户端的socket
socket还可以提供不同类型的通信功能(本地、局域网等)
头文件:
#include
#include
#include
#include
#include
#include
#include
定义服务端的socket和sockaddr_inint server = 0; struct sockaddr_in saddr = {0};
定义要通信的客户端的socket和sockaddr_inint client = 0; struct sockaddr_in caddr = {0};
定义客户端sockaddr_in 的长度变量socklen_t asize = 0;
以及其他的变量
int len = 0;//发送和接收的字符串长度
char buf[32] = {0};//接收区
int r = 0;//循环控制变量
创建socket
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
赋值服务端的sockaddr_in
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:同一wifi下的主机地址都可以
saddr.sin_port = htons(8899);
绑定以及监听
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 1) == -1 )
{
printf("server listen error\n");
return -1;
}
等待客户端的连接
while(1)
{
asize = sizeof(caddr);//客户端sockaddr_in大小
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
printf("client accept error\n");
return -1;
}
printf("client: %d\n", client);
}
接收和发送数据:
do
{
r = recv(client, buf, sizeof(buf), 0);
if( r > 0 )
{
printf("Receive: %s\n", buf);
if( strcmp(buf, "quit") != 0 )//当字符串为quit时,退出
{
len = send(client, buf, r, 0);
break;
}
else
{
break;
}
}
} while ( r > 0 );
注意:这里的代码在while(1)里面
代码全貌:
#include
#include
#include
#include
#include
#include
#include
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int len = 0;server
char buf[32] = {0};
int r = 0;
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1 )
{
printf("server socket error\n");
return -1;
}
saddr.sin_fami0ly = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind error\n");
return -1;
}
if( listen(server, 1) == -1 )
{
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while( 1 )
{
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if( client == -1 )
{
printf("client accept error\n");
return -1;
}
printf("client: %d\n", client);
do
{
r = recv(client, buf, sizeof(buf), 0);
if( r > 0 )
{
printf("Receive: %s\n", buf);
if( strcmp(buf, "quit") != 0 )
{
len = send(client, buf, r, 0);
break;
}
else
{
break;
}
}
} while ( r > 0 );
close(client);
}
close(server);
return 0;
}
复用上期的服务端
改动:
1、定义input变量,让用户自己输入字符发送给服务端char input[32] = {0};
2、端口号和ip地址
ip地址:在TerMinal中输入ifconfig
可以看到ip地址。如下图
端口号:要和服务器中的一样(8899)
收发数据代码:
while(1)
{
printf("Input: ");
scanf("%s", input);
len = send(sock, input, strlen(input) + 1, 0);
r = recv(sock, buf, sizeof(buf), 0);
if( r > 0 )
{
printf("Receive: %s\n", buf);
}
else
{
break;
}
}
客户端代码全貌:
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sock = 0;
struct sockaddr_in addr = {0};
int len = 0;
char buf[128] = {0};
char input[32] = {0};
int r = 0;
sock = socket(PF_INET, SOCK_STREAM, 0);
if( sock == -1 )
{
printf("socket error\n");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.254.128");
addr.sin_port = htons(8899);
if( connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1 )
{
printf("connect error\n");
return -1;
}
printf("connect success\n");
while( 1 )
{
printf("Input: ");
scanf("%s", input);
len = send(sock, input, strlen(input) + 1, 0);
r = recv(sock, buf, sizeof(buf), 0);
if( r > 0 )
{
printf("Receive: %s\n", buf);
}
else
{
break;
}
}
close(sock);
return 0;
}
大家可以使用下面2个命令编译我们写好的东西:
gcc -o 编译后的文件名.o 编译的文件名.c
./编译后的文件名.o
注意:要先开服务端!!!