我们就可以简单的理解 HTTP 的数据结构了。
分享更多网络底层原理知识点,内容包括Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等。后台私信【架构】获取
Linux下的socket演示程序下面用最基础的Socket来进行服务端与客户端的交互,让你理解的更为清晰。
接口详解:
方法名 | 用途 |
socket(): | 创建socket |
bind(): | 绑定socket到本地地址和端口,通常由服务端调用 |
listen(): | TCP专用,开启监听模式 |
accept(): | TCP专用,服务器等待客户端连接,一般是阻塞态 |
connect(): | TCP专用,客户端主动连接服务器 |
send(): | TCP专用,发送数据 |
recv(): | TCP专用,接收数据 |
sendto(): | UDP专用,发送数据到指定的IP地址和端口 |
recvfrom(): | UDP专用,接收数据,返回数据远端的IP地址和端口 |
close(): | 关闭socket |
使用Socket进行网络通信的过程
① 服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户的连接请求。
② 客户程序根据服务器程序所在的主机和端口号发出连接请求。
③ 如果一切正常,服务器接受连接请求。并获得一个新的绑定到不同端口地址的套接字。
④ 客户和服务器通过读、写套接字进行通讯。
客户机/服务器模式
在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式*(client/server),即客户像服务其提出请求,服务器接受到请求后,提供相应的服务。
服务器:
(1)首先服务器方要先启动,打开一个通信通道并告知本机,它愿意在某一地址和端口上接收客户请求
(2)等待客户请求到达该端口
(3)接收服务请求,处理该客户请求,服务完成后,关闭此新进程与客户的通信链路,并终止
(4)返回第二步,等待另一个客户请求
(5)关闭服务器
客户方:
(1)打开一个通信通道,并连接到服务器所在的主机特定的端口
(2)向服务器发送请求,等待并接收应答,继续提出请求
(3)请求结束后关闭通信信道并终止
具体实现,新建服务端socket_server_tcp.c
具体代码如下: socket_server_tcp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define PORT 3040 //端口号
#define BACKLOG 5 //最大监听数
int main() {
int iSocketFD = 0; //socket句柄
int iRecvLen = 0; //接收成功后的返回值
int new_fd = 0; //建立连接后的句柄
char buf[4096] = {0}; //
struct sockaddr_in stLocalAddr = {0}; //本地地址信息结构图,下面有具体的属性赋值
struct sockaddr_in stRemoteAddr = {0}; //对方地址信息
socklen_t socklen = 0;
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket SOCK_STREAM代表以tcp方式进行连接
if (0 > iSocketFD) {
printf("创建socket失败!\n");
return 0;
}
stLocalAddr.sin_family = AF_INET; /*该属性表示接收本机或其他机器传输*/
stLocalAddr.sin_port = htons(PORT); /*端口号*/
stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); /*IP,括号内容表示本机IP*/
//绑定地址结构体和socket
if (0 > bind(iSocketFD, (void *) &stLocalAddr, sizeof(stLocalAddr))) {
printf("绑定失败!\n");
return 0;
}
//开启监听 ,第二个参数是最大监听数
if (0 > listen(iSocketFD, BACKLOG)) {
printf("监听失败!\n");
return 0;
}
printf("iSocketFD: %d\n", iSocketFD);
//在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小
new_fd = accept(iSocketFD, (void *) &stRemoteAddr, &socklen);
if (0 > new_fd) {
printf("接收失败!\n");
return 0;
} else {
printf("接收成功!\n");
//发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可)
send(new_fd, "这是服务器接收成功后发回的信息!", sizeof("这是服务器接收成功后发回的信息!"), 0);
}
printf("new_fd: %d\n", new_fd);
iRecvLen = recv(new_fd, buf, sizeof(buf), 0);
if (0 >= iRecvLen) //对端关闭连接 返回0
{
printf("对端关闭连接或者接收失败!\n");
} else {
printf("buf: %s\n", buf);
}
close(new_fd);
close(iSocketFD);
return 0;
}
新建客户端端socket_client_tcp.c socket_client_tcp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define PORT 3040 //目标地址端口号
#define ADDR "10.6.191.177" //目标地址IP
int main() {
int iSocketFD = 0; //socket句柄
unsigned int iRemoteAddr = 0;
struct sockaddr_in stRemoteAddr = {0}; //对端,即目标地址信息
socklen_t socklen = 0;
char buf[4096] = {0}; //存储接收到的数据
iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket
if (0 > iSocketFD) {
printf("创建socket失败!\n");
return 0;
}
stRemoteAddr.sin_family = AF_INET;
stRemoteAddr.sin_port = htons(PORT);
inet_pton(AF_INET, ADDR, &iRemoteAddr);
stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
//连接方法: 传入句柄,目标地址,和大小
if (0 > connect(iSocketFD, (void *) &stRemoteAddr, sizeof(stRemoteAddr))) {
printf("连接失败!\n");
//printf("connect failed:%d",errno);//失败时也可打印errno
} else {
printf("连接成功!\n");
recv(iSocketFD, buf, sizeof(buf), 0); ////将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。
printf("Received:%s\n", buf);
}
close(iSocketFD);//关闭socket
return 0;
}
下面是我的编译及运行效果:
编译命令如下:
gcc -o server socket_server_tcp.c
gcc -o client socket_client_tcp.c
#运行命令
./server #首先启动
./client #次之启动
基于UDP协议实现CS端
**基于UDP(面向无连接)的socket编程——**数据报式套接字(SOCK_DGRAM) 网络间通信AF_INET,典型的TCP/IP四型模型的通信过程
服务器:(多线程的【每10秒会打印一行#号】 与 循环监听) socket_server_udp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
void * test(void *pvData)
{
while(1)
{
sleep(5);
printf("################################\n");
}
return NULL;
}
int main(void)
{
pthread_t stPid = 0;
int iRecvLen = 0;
int iSocketFD = 0;
char acBuf[4096] = {0};
struct sockaddr_in stLocalAddr = {0};
struct sockaddr_in stRemoteAddr = {0};
socklen_t iRemoteAddrLen = 0;
/* 创建socket */
iSocketFD = socket(AF_INET, SOCK_DGRAM, 0);
if(iSocketFD < 0)
{
printf("创建socket失败!\n");
return 0;
}
/* 填写地址 */
stLocalAddr.sin_family = AF_INET;
stLocalAddr.sin_port = htons(12345);
stLocalAddr.sin_addr.s_addr = 0;
/* 绑定地址 */
if(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr)))
{
printf("绑定地址失败!\n");
close(iSocketFD);
return 0;
}
pthread_create(&stPid, NULL, test, NULL); //实现了多线程
while(1) //实现了循环监听
{
iRecvLen = recvfrom(iSocketFD, acBuf, sizeof(acBuf), 0, (void *)&stRemoteAddr, &iRemoteAddrLen);
printf("iRecvLen: %d\n", iRecvLen);
printf("acBuf:%s\n", acBuf);
}
close(iSocketFD);
return 0;
}
客户端: socket_client_udp.c
//
// Created by android on 19-8-9.
//
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
int main(void)
{
int iRecvLen = 0;
int iSocketFD = 0;
int iRemotAddr = 0;
char acBuf[4096] = {0};
struct sockaddr_in stLocalAddr = {0};
struct sockaddr_in stRemoteAddr = {0};
socklen_t iRemoteAddrLen = 0;
/* 创建socket */
iSocketFD = socket(AF_INET, SOCK_DGRAM, 0);
if(iSocketFD < 0)
{
printf("创建socket失败!\n");
return 0;
}
/* 填写服务端地址 */
stLocalAddr.sin_family = AF_INET;
stLocalAddr.sin_port = htons(12345);
inet_pton(AF_INET, "10.6.191.177", (void *)&iRemotAddr);
stLocalAddr.sin_addr.s_addr = iRemotAddr;
iRecvLen = sendto(iSocketFD, "这是一个测试字符串", strlen("这是一个测试字符串"), 0, (void *)&stLocalAddr, sizeof(stLocalAddr));
close(iSocketFD);
return 0;
}
测试:
1、编译服务器:因为有多线程,所以服务器端进程要进行pthread编译
gcc socket_server_udp.c -pthread -g -o server_udp #客户端和上方相同
复制代码
执行结果如下:
右下为客户端重复执行
服务器端有主线程和辅线程,主线程,打印客户端发送的请求;辅线程每隔5秒钟打印一排#号。
,