本文主要涉及网络编程的具体实现过程,实现客户端与服务端的TCP的信息传输,注意还只是单向的客户端发送,服务端接收。
让我更详细地描述服务端通信流程
初始化:
使用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);
socket
: 创建一个新的套接字。返回一个文件描述符。
bind
: 将套接字绑定到特定的IP地址和端口。
listen
: 将套接字设为监听模式,等待客户端连接。
accept
: 等待并接受客户端的连接请求,返回一个新的套接字描述符。
pthread_create
: 创建新的线程。
recv
: 从已连接的套接字接收数据。
send
: 发送数据到已连接的套接字。
close
: 关闭套接字或文件描述符。
通过这些详细的步骤和函数,服务端能够建立与客户端的连接,为每个客户端创建独立的处理线程,并进行有效的数据交换。
//服务器
#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;
}
以下是客户端进行通信的详细流程及其关键函数:
初始化:
创建套接字:
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
函数关闭套接字,释放资源并结束程序。
这就是客户端进行通信的完整流程。它首先连接到服务器,然后进入一个循环,允许用户发送和接收消息,最后关闭连接并退出。
/*
* @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;
}