今天我们来介绍linux下的socket编程,先给大家看张图。
所谓的socket,简单说就是两个程序经过双向通信而实现数据的交换,有发出信息的服务端,当然就有接受信息的客户端,分别为socket_server.c和socket_client.c两个文件,大家先来看看服务端,注意头文件
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
int listen_fd, new_fd = -1;
struct sockaddr_in serv_addr;
char buf[1024];
listen_fd = socket(AF_INET, SOCK_STREAM, 0); //我们用socket来申请通信端口,三个参数一为协议域,AF_INET决定用IPv4地址;二为指定socket类型,SOCK_STREAM即对于TCP协议来说的面向流的传输协议;三为指定协议编号,为0则选择二类型中对应的默认协议
if(listen_fd < 0 ) //如果申请失败,打印错误信息
{
printf("create socket failure: %s\n", strerror(errno));
return -1;
}
printf("socket create fd[%d]\n", listen_fd);
memset(&serv_addr, 0, sizeof(serv_addr)); //用之前先清零,后同
serv_addr.sin_family = AF_INET; //表示TCP/IP协议,IP地址为IPv4
serv_addr.sin_port = htons(8889); //设置端口号为8889,s指无符号短整型,即把我们机器上的字节序转换为大端字节序
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //l指无符号长整型,INADDR_ANY表示监听本机所有IP
if( bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0 ) //bind用于绑定IP地址和端口号到socket,失败打印错误原因
{
printf("create socket failure: %s\n", strerror(errno));
return -2;
}
printf("socket bind ok\n", listen_fd);
listen(listen_fd, 13); //对端口进行监听,13为最大连接个数
printf("listen fd ok\n", listen_fd);
while(1)
{
printf("start accept...\n", listen_fd);
new_fd = accept(listen_fd, NULL, NULL);
//调用accept阻塞,等待客户端连接
if(new_fd < 0)
{
printf("accept new socket failure: %s\n", strerror(errno));
return -2;
}
printf("accept ok, return new fd: [%d]\n", new_fd);
memset(buf, 0, sizeof(buf));
read(new_fd, buf, sizeof(buf)); //read也阻塞,直到读取客服端传来信息
printf("read '%s' from client\n", buf);
write(new_fd, "goodbye", strlen("goodbye"));
sleep(1);
close(new_fd); //记得关闭相应描述字
}
close(listen_fd);
}
有了起决定作用的服务端,当然也要有与之连接的客户端
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
int conn_fd = -1;
struct sockaddr_in serv_addr;
char buf[1024];
conn_fd = socket(AF_INET, SOCK_STREAM, 0);
if(conn_fd < 0)
{
printf("create socket failure: %s\n", strerror(errno));
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8889); //这里的设置和服务端相同
inet_aton( "127.0.0.1", &serv_addr.sin_addr ); //inet_aton将字符串转为整数,IP地址"127.0.0.1"表示自己连自己
if( connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) //通过connect与服务端进行连接,失败打印错误原因
{
printf("connect to server failure: %s\n", strerror(errno));
return 0;
}
write(conn_fd, "hello world!", strlen("hello world!"));
memset(buf, 0, sizeof(buf));
read(conn_fd, buf, sizeof(buf)); //阻塞,直到服务端回复消息
printf("read '%s' from server\n", buf);
sleep(1);
close(conn_fd);
}
相信上面两个程序已经注释得比较清楚了,我这里不再废话,直接来执行程序。先执行服务端,在没有执行客户端client的情况下,服务端server是一直阻塞在这里的
[lingyun@localhost file]$ ./socket_server
socket create fd[3]
socket bind ok
listen fd ok
start accept...
同时,我们最好在secureCRT克隆的一个新窗口下运行客户端client,看看服务端在运行的程序有何变化
[lingyun@localhost file]$ ./socket_client
read 'goodbye' from server
在上面的客户端client程序中,我们收到了来自服务端server的goodbye,而在下面的服务端中我们也收到客户端的hello world!
说明我们成功地完成了双方的通信。
[lingyun@localhost file]$ ./socket_server
socket create fd[3]
socket bind ok
listen fd ok
start accept...
accept ok, return new fd: [4]
read 'hello world!' from client
start accept...
当然,这是个只能完成与单一客户端通信的低级服务端,假如百度就回答你一个人的问题那百度可不玩完,以后我们将要介绍将之前的多线程应用到现在的socket中。