socket通信基础讲解及示例-C

服务端与客户端

简介

 服务端与客户端是计算机网络通信的两个主要角色,在物理上并没有什么区别,可以同时在一个设备,也可在不同的设备上。只不过在市场上,为了性能和更好的提供服务。大部分的服务端与客户端不在同个设备中。
客户端(client)一般指通过应用程序(例如手机app,电脑软件)或浏览器网页向服务器获取资源和数据的计算机或设备。
服务端(server)一般指提供服务的计算机或设备。服务端接收并处理客户端的请求,并返回对应的结果。
服务器指服务端所部署的计算机或设备

socket通信

以下均在linux系统中实现,socket通信是进程通信的一种方式。以下操作函数的头文件位于socket.h

服务端与客户端通信模型

在这里插入图片描述

通信实战

server(服务端)创建

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>


int main()
{

	int socketfd = socket(AF_INET, SOCK_STREAM, 0);
	if(socketfd < 0)
	{
		printf("creat socket error\n");
		return -1;
	}
	
	struct sockaddr_in socket_addr;
	memset(&socket_addr, 0, sizeof(socket_addr)); 
	 
	socket_addr.sin_family = AF_INET;
	socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	socket_addr.sin_port = htons(4017);
	
	if(bind(socketfd, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
	{
		printf("bind port error\n");
		return -1;
	}

	if(listen(socketfd, 5) < 0)
	{
		printf("listen error\n");
		return -1;
	}
	
	struct sockaddr_in client_addr;
	int clilen = sizeof(struct sockaddr_in);
	int clifd = accept(socketfd, (struct sockaddr*)&client_addr, (socklen_t*)&clilen);
	
	char buf[256] = {0};
	char sendbuf[] = "hello";
	while(1)
	{
		memset(buf, 0, 	sizeof(buf));
		read(clifd, buf, 255);
		printf("recv message is: %s\n", buf);
		write(clifd, sendbuf, 5);
		sleep(1);
	}
	
	close(clifd);
	close(socketfd);
	
	return 0;
}

client(客户端)创建

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>


int main()
{

	int socketfd = socket(AF_INET, SOCK_STREAM, 0);
	if(socketfd < 0)
	{
		printf("creat socket error\n");
		return -1;
	}
	
	struct sockaddr_in socket_addr;
	memset(&socket_addr, 0, sizeof(socket_addr)); 
	 
	socket_addr.sin_family = AF_INET;
	socket_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	socket_addr.sin_port = htons(4017);
	
	if(connect(socketfd, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
	{
		printf("bind port error\n");
		return -1;
	}

	char buf[256] = {0};
	char sendbuf[] = "client msg";
	while(1)
	{
		memset(buf, 0, 	sizeof(buf));
		read(socketfd, buf, 255);
		printf("recv message is: %s\n", buf);
		write(socketfd, sendbuf, 11);
		sleep(1);
	}
	
	close(socketfd);
	
	return 0;
}

函数详解

创建套接字 socket

  • 函数原型
int socket(int domain, int type, int protocol)
  • 函数解析
    作用 :用于创建一个指定协议的sockfd,即一个套接字。

例如创建一个tcp,ipv4的sockfd

int socketfd = socket(AF_INET, SOCK_STREAM, 0);

返回: 创建失败返回-1,成功返回文件描述符。

  • 参数解析
    以下枚举值只列出部分
参数说明枚举
domain指定通信域,即选择通信用的协议族AF_INET:ipv4
AF_INET6:ipv6
type指定套接字类型SOCK_STREAM:提供有序的、可靠的、双向的、基于连接的字节流,流式协议。
SOCK_DGRAM:固定长度的、无连接的、不可靠的报文传递,报式协议
protocol具体的一个协议一般写0
流式协议默认是TCP
报式协议默认是UDP

绑定端口bind

  • 函数原型
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 函数解析
    作用 :将socket创建的socketfd与地址addr进行绑定。大部分用来绑定端口。一般在于服务端使用,如果客户端在需要绑定端口的情况下,也可以使用。

例如将刚刚创建的socketfd与本地端口4017进行绑定

struct sockaddr_in socket_addr;
memset(&socket_addr, 0, sizeof(socket_addr)); 
 
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socket_addr.sin_port = htons(4017);

bind(socketfd, (struct sockaddr *)&socket_addr, sizeof(socket_addr));

返回:成功返回0,一般端口被其他进程占用时,才会返回失败!

  • 参数解析
参数说明
sockfd创建的套接字
sockaddr地址,结构体中包含了协议族,端口号以及地址ip
socklen_t地址所指向的结构体的地址长度

进入监听状态listen

  • 函数原型
int listen(int sockfd, int backlog);
  • 函数解析
    作用 :让服务端进入监听状态,等待客户端的连接。用于服务端的进程。需在accept之前使用

例如 最大监听客户端数量为5

listen(socketfd, 5);

返回
成功返回0

  • 参数解析
参数说明
sockfd创建的套接字
backlog服务端等待连接的最大数量(即半连接和全连接最大之和)

获取客户端连接请求accept

  • 函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 函数解析
    作用 :获取客户端的连接请求并建立连接

例如

struct sockaddr_in client_addr;
int clilen = sizeof(struct sockaddr_in);

int clifd = accept(socketfd, (struct sockaddr*)&client_addr, (socklen_t*)&clilen);

返回:成功返回客户端的socketfd,后期通过该socketfd与此客户端进行通信。失败返回-1

  • 参数解析
参数说明
sockfd用于监听的文件描述符
addr用于返回监听到的客户端的地址信息
addrlenaddr的字节大小

接收网络数据read

  • 函数原型
ssize_t read(int fd, void *buf, size_t count);
  • 函数解析
    作用 :接收网络数据

例如获取从clientfd中接收到的网络数据

char buf[256] = {0};
read(clientfd, buf, 256);

返回:成功返回接收到的网络数据长度;失败返回<0。
头文件:unistd.h

  • 参数解析
参数说明
sockfd需要获取数据的对象socketfd
buf接收到的网络数据
count限制字节接收长度。
当缓冲区长度大于该长度时,返回count长度字节;
档缓冲区长度小于该长度时,返回实际长度的字节

发送数据write

  • 函数原型
ssize_t write(int fd, const void *buf, size_t count);
  • 函数解析
    作用 :用于发送数据

例如 :对clientfd发送abc

char sendbuf[] = “hello”;
write(clientfd, sendbuf, 3);

返回:成功返回成功写入的字节数,失败返回-1
头文件:unistd.h

  • 参数解析
参数说明
fd需要发送数据的对象socketfd
buf发送的数据
count当实际发送数据长度大于count时,按count数量字节数发送,否则按实际长度发送

读取和写入除了read和write外,还有recv和send;recvmsg和sendmsg等。

关闭套接字close

  • 函数原型
int close(int fd);
  • 函数解析
    作用 :关闭指定套接字

例如

close(clientfd);

返回:成功返回0

  • 参数解析
参数说明
fd需要关闭的套接字

连接指定服务端connect

  • 函数原型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

注意 客户端使用

  • 函数解析
    作用 :在tcp中用于跟服务端建立连接,而在udp中仅仅是在本地对服务端的IP地址和端口与创建的sockfd进行绑定记录

例如 连接ip地址为127.0.0.1(本地回环),且端口号为4017的服务端进程

struct sockaddr_in socket_addr;
memset(&socket_addr, 0, sizeof(socket_addr)); 
 
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socket_addr.sin_port = htons(4017);

connect(sockfd, (struct sockaddr *)&socket_addr,sizeof(socket_addr));

返回:成功返回0,失败返回-1

inet_addr所在头文件为:arpa/inet.h

  • 参数解析
参数说明
sockfd用于通信的文件描述符
addr客户端所要连接的服务端的地址信息
addrlenaddr的内存大小
  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值