1 服务器端的socket编程
服务器端主要的功能,就是创建socket监听,等待客户端的连接。
基础的步骤很简单,如下:
(1) 创建socket;
(2) 绑定地址和端口;
(3) 启动监听;
(4) 接受客户端请求;
(5) 读/写数据;
1.1 创建socket:
函数原型:
mysocket = socket(int socket_family, int socket_type, int protocol);
1
第一个参数为socket的协议族,由一组宏定义表示,一般使用AF_INET,表示使用IPV4协议。
第二个参数为socket的类型,主要分为SOCK_STREAM和SOCK_DGRAM,分别对应TCP协议和UDP协议。
第三个参数为socket使用的特定协议,一般对应一个协议族就只有一个协议,可以使用0表示使用默认的协议。
函数返回值为创建的socket文件描述符。-1表示创建失败。
函数定义及相关宏定义在头文件<sys/socket.h>。
1.2 绑定地址和端口:
函数原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
1
第一个参数为刚刚创建的socket;
第二个参数是一个结构体sockaddr的指针。但实际使用的时候,根据协议的不同,而使用不同的结构。对于TCP来说,使用sockaddr_in。
第三个参数是第二个参数的结构体的长度。
sockaddr_in的结构为:
struct sockaddr_in
{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[sizeof (struct sockaddr) -
(sizeof (unsigned short int)) -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
//里面的in_addr结构体:
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
1.3 启动监听:
函数原型:
int listen(int sockfd, int backlog);
1
第一个参数为socket文件描述符;
第二个参数为同时连接数量;
1.4 接受客户端请求:
函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
1
第一个参数为socket文件描述符;
第二个参数为sockaddr结构体指针,用来接收客户端的地址信息。
第三个参数为第二个参数的长度。
返回值为对应的客户端socket的文件描述符,失败则返回-1。
如果没有特殊设置的话,accept会阻塞,直接有连接进来或者出错。
1.5 读/写数据:
有连接进来之后,就可以使用read/write函数进行通讯了。
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
1.6 一个简单的服务器例子:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 5116
#define BUF_SIZE 256
int main(int argc, char** argv)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1){
printf("created socket error\n");
return 0;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;//表示使用本机IP
int len = sizeof(addr);
if (-1 == bind(fd, (struct sockaddr*)&addr, len)){
printf("bind socket fail\n");
close(fd);
return 0;
}
listen(fd, 10);
int clientFd;
struct sockaddr_in clientAddr;
int clientLen = sizeof(clientAddr);
printf("The server is waiting for connect...\n");
clientFd = accept(fd, (struct sockaddr*)&clientAddr, &clientLen);
char szMsg[BUF_SIZE];
int n;
if (clientFd > 0){
printf("Accept client from %s:%d\n", inet_ntoa(clientAddr.sin_addr), clientAddr.sin_port);
while((n = read(clientFd, szMsg, BUF_SIZE)) > 0){
szMsg[n] = 0;
printf("Recv Msg : %s\n", szMsg);
}
close(clientFd);
}
close(fd);
return 0;
}
使用gcc编译,就可以运行,等待客户端的连接了。
2 客户端的socket编程
客户端的功能,就是连接到服务器,然后就可以进行通讯。所以主要的步骤就是填写服务器的地址并进行连接。
(1)创建socket;
(2)连接服务器;
(3)读/写数据;
2.1 连接服务器
创建socket与读/写数据,和服务端的一模一样,这里只讲一下连接这一步。
函数原型:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
1
第一个参数为创建的socket文件描述符。
第二个参数为sockaddr结构体指针,服务器端的地址信息。
第三个参数为第二个参数的长度。
可以使用inet_addr将IP地址从字符串形式转换为二进制形式。
2.2 简单的客户端实例:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 5116
#define BUF_SIZE 256
int main(int argc, char** argv)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = ntohs(PORT);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int len = sizeof(addr);
if (-1 == connect(fd, (struct sockaddr*)&addr, len)){
printf("Connect to server fail\n");
return 0;
}
char szMsg[BUF_SIZE];
while(scanf("%s", szMsg)){
if (!strcmp(szMsg, "quit")){
break;
}
write(fd, szMsg, strlen(szMsg));
}
close(fd);
return 0;
}
3 一些总结
在linux中,一切都是文件,所以socket也和普通文件一样,用文件描述符表示,使用read/write读写,使用close关闭。
对于一般工程而言,服务端会使用epoll之类的来处理多个客户端的连接,保证高效正确的运行。
————————————————
版权声明:本文为CSDN博主「看热闹的咸鱼」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lch_boy/article/details/43958861
来源:CSDN
作者:苞米地里捉小鸡
链接:https://blog.csdn.net/weixin_42709632/article/details/104791940