网络编程——实现服务端与客户端TCP的消息发送与接收
网络编程——实现服务端与客户端TCP的消息发送与接收
本文主要涉及网络编程的具体实现过程,实现客户端与服务端的TCP的信息传输,注意还只是单向的客户端发送,服务端接收。
文章目录
一、 服务端
1.1 服务端通信详细流程
让我更详细地描述服务端通信流程
-
初始化:
-
使用
socket
函数创建一个服务器套接字。sockfd = socket(AF_INET, SOCK_STREAM, 0);
-
使用
bind
函数将服务器套接字绑定到指定的IP地址和端口。bind(sockfd, (struct sockaddr *)&sa, sizeof(sa));
-
使用
listen
函数开始监听连接请求。listen(sockfd, 5); // 5 是监听队列的最大长度
-
-
等待客户端连接:
-
使用
accept
函数等待客户端连接,并返回一个新的套接字描述符。int accceptfd = accept(sockfd, (struct sockaddr *)&sa, &addrlen);
-
-
为客户端创建处理线程:
-
为每个新连接创建一个线程,使用
pthread_create
。pthread_create(&thread, NULL, ClinetFunction, (void *)&accceptfd);
-
-
与客户端通信:
-
在
ClinetFunction
中使用recv
函数接收客户端发送的数据。recv(accceptfd, buf, sizeof(buf), 0);
-
处理或响应接收到的数据。
-
使用
send
函数将处理后的数据发送回客户端。send(accceptfd, sendbuf, strlen(sendbuf) + 1, 0);
-
-
关闭连接:
-
使用
close
函数关闭与客户端的连接。close(accceptfd);
-
1.2 关键函数及其详细描述:
-
socket
: 创建一个新的套接字。返回一个文件描述符。- 参数:协议族(AF_INET表示IPv4)、套接字类型(SOCK_STREAM表示TCP)、协议编号(通常为0)。
-
bind
: 将套接字绑定到特定的IP地址和端口。- 参数:套接字描述符、指向地址结构的指针、地址结构的大小。
-
listen
: 将套接字设为监听模式,等待客户端连接。- 参数:套接字描述符、监听队列的最大长度。
-
accept
: 等待并接受客户端的连接请求,返回一个新的套接字描述符。- 参数:套接字描述符、指向客户端地址结构的指针、地址结构的大小。
-
pthread_create
: 创建新的线程。- 参数:指向线程ID的指针、线程属性(通常为NULL)、线程函数、传递给线程函数的参数。
-
recv
: 从已连接的套接字接收数据。- 参数:套接字描述符、缓冲区、缓冲区的大小、标志。
-
send
: 发送数据到已连接的套接字。- 参数:套接字描述符、要发送的数据、数据的大小、标志。
-
close
: 关闭套接字或文件描述符。- 参数:套接字或文件描述符。
通过这些详细的步骤和函数,服务端能够建立与客户端的连接,为每个客户端创建独立的处理线程,并进行有效的数据交换。
1.3 服务端完整代码
//服务器
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
//全局的套接字
int sockfd = -1;
//4 循环等待客户端的连接,如果没有连接则等待,如果有连接则返回一个连接套接字
//5 和客户端进行正常收发
//6 弄完了关闭套接字
//将套接字创建好 并且绑定 监听
//将ip地址和端口号传进来 端口号释放需要时间(轮询机制)
void TcpInit(const char * ipaddr,unsigned short port)
{
//1 创建套接字 ---- 神马都是文件,因此你的网络通信也是一个文件
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd)
{
perror("server socket error");
exit(1);//没有必要运行了
}
//2 我们需要将服务器的IP地址绑定到套接字
struct sockaddr_in sa;
sa.sin_family = AF_INET;//协议族
sa.sin_port = htons(port);//端口号 内存是小端的 我们要转大端
sa.sin_addr.s_addr = inet_addr(ipaddr);//将我们点分式的ip地址转换为一个大端整数
//printf("ipaddr = %x port = %x\n",inet_addr(ipaddr),htons(port));
int r = bind(sockfd,(struct sockaddr *)&sa,sizeof(sa));
if(-1 == r)
{
perror("server bind error");
exit(2);//没有必要运行了
}
//3 监听连接 ---- 创建一个监听队列 建立5个10个可以了
listen(sockfd,5);
}
//专门用于去服务一个客户的线程
void * ClinetFunction(void * arg)
{
pthread_detach(pthread_self());//将其分离
int * accceptfd = (int *)arg;
printf(" * accceptfd = %d\n", * accceptfd);
char buf[1024] = {0};
//你发什么信息过来 我就在这个信息之前加上一节 然后回发给你
while(1)
{
char sendbuf[1024] = {"SB250->"};
int r = recv(*accceptfd,buf,1024,0);//阻塞等待数据过来
if(-1 == r)
{
perror("recv error");
break;
}
else if(0 == r)//客户端已经断了
{
printf("对方断开连接了\n");
break;
}
else//接收到信息了
{
printf("recv buf = %s\n",buf);
strcat(sendbuf,buf);
send(*accceptfd,sendbuf,strlen(sendbuf) + 1,0);
}
}
close(*accceptfd);
free(accceptfd);
}
//等待客户端的连接
void waitconnect(void)
{
//我们要基于这个连接套接字去通信
struct sockaddr_in sa;
socklen_t addrlen = sizeof(sa);
while(1)
{
printf("一直等待对方的连接.......\n");
int * accceptfd = malloc(4);//避免释放 因此我们要动态内存分配才可以
*accceptfd = accept(sockfd,(struct sockaddr *)&sa,&addrlen);
printf("连接者为:%s %d\n",inet_ntoa(sa.sin_addr),ntohs(sa.sin_port));
//开一个线程出去 让它去服务与我的连接
pthread_t thread;
if(pthread_create(&thread,NULL,ClinetFunction,(void *)accceptfd) != 0)
{
perror("create thread error");
continue;
}
}
close(sockfd);
}
//通过main函数的参数 我们将这个ip地址和端口给进去
//./a.out 192.168.31.251 8888
int main(int argc,char * argv[])
{
if(argc < 3)
{
printf("参数都不齐\n");
return -1;
}
TcpInit(argv[1],atoi(argv[2]));
waitconnect();
return 0;
}
二、 客户端
2.1 客户端通信详细流程
以下是客户端进行通信的详细流程及其关键函数:
-
初始化:
-
创建套接字:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
使用
socket
函数创建一个IPv4的TCP套接字。 -
填充服务器地址信息:
struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = inet_addr(ipaddr);
这里设置了服务器的IP地址、端口号,并将其转换为网络字节序。
-
连接到服务器:
int r = connect(sockfd, (struct sockaddr *)&sa, sizeof(sa));
使用
connect
函数与服务器建立连接。
-
-
通信循环:
-
从用户那里获取要发送的消息:
printf("输入你要发送的信息(输入quit退出):"); fflush(stdout); scanf("%s", buf);
这里等待用户输入消息。
-
检查用户是否想退出:
if(!strcmp(buf,"quit")) { break; }
如果用户输入了“quit”,则退出循环。
-
发送消息到服务器:
send(sockfd, buf, strlen(buf) + 1, 0);
使用
send
函数将用户输入的消息发送到服务器。 -
接收来自服务器的响应:
recv(sockfd, buf, 1024, 0);
使用
recv
函数从服务器接收响应。 -
打印服务器的响应:
printf("服务器回我的信息是:%s\n", buf);
将接收到的消息打印到控制台。
-
-
关闭连接:
-
关闭套接字:
close(sockfd);
使用
close
函数关闭套接字,释放资源并结束程序。
-
这就是客户端进行通信的完整流程。它首先连接到服务器,然后进入一个循环,允许用户发送和接收消息,最后关闭连接并退出。
2.2 完整代码
/*
* @Description: 客户端代码
* @Version: 1.0
* @Autor: Huining Li777
* @Date: 2023-12-26 09:07:23
* @LastEditors: 李慧宁-Huining Li777
* @LastEditTime: 2023-12-26 12:46:58
*/
//客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
//全局的套接字
int sockfd = -1;
//将套接字创建好 并且绑定 监听
//将ip地址和端口号传进来 端口号释放需要时间(轮询机制)
void TcpInit(const char * ipaddr,unsigned short port)
{
//1 创建套接字 ---- 神马都是文件,因此你的网络通信也是一个文件
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd)
{
perror("server socket error");
exit(1);//没有必要运行了
}
//2 我们需要将服务器的IP地址绑定到套接字
struct sockaddr_in sa;
sa.sin_family = AF_INET;//协议族
sa.sin_port = htons(port);//端口号 内存是小端的 我们要转大端
sa.sin_addr.s_addr = inet_addr(ipaddr);//将我们点分式的ip地址转换为一个大端整数
//连接服务器
int r = connect(sockfd,(struct sockaddr *)&sa,sizeof(sa));
if(-1 == r)
{
perror("connect error");
exit(2);
}
}
//开始正常的收发
void function(void)
{
char buf[1024] = {0};
while(1)
{
printf("输入你要发送的信息(输入quit退出):");
fflush(stdout);
scanf("%s",buf);
if(!strcmp(buf,"quit"))
{
break;
}
send(sockfd,buf,strlen(buf) + 1,0);
recv(sockfd,buf,1024,0);
printf("服务器回我的信息是:%s\n",buf);
}
}
int main(int argc,char * argv[])
{
if(argc < 3)
{
printf("参数都不齐\n");
return -1;
}
TcpInit(argv[1],atoi(argv[2]));
function();
close(sockfd);
return 0;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!