版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hello_world12138/article/details/48710919
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">关于网络编程,socket编程,TCP,客户端,服务器C/S架构编程思路</span>
学习这个我感觉是看着困难,理解了以后,原理还是能够 接受的,有什么不对的地方还请大牛们指正。
下面给出的知识服务器和客户端能够互发信息,在此基础上可以在客户端和服务器添加线程,这样就是即时通讯软件项目的前身了,服务器通过每个客户端的fd,确认是哪个客户端,从而准确的发送和接收消息,最终能够做到客户端和客户端之间互发信息。
头文件:
#ifndef __TCP_NET__SOCKET__H
#define __TCP_NET__SOCKET__H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#endif
客户端:
客户端负责的内容较少,主要是建立套接字,然后发送连接请求,如果连接成功了,那么下面的就可以发送和接收了。
1.首先,执行可执行文件时,需要给出相应的ip用于连接。
2.接着,申请一个流式套接字,得到描述符sfd
3.把需要连接的服务器的网络的ip地址族,端口号,ip都和该套接字建立关联关系
4.接着就向服务器发送数据
#include "5.tcp_net_socket.h"
//公用的端口号
#define PORTNUMBER 2333
//连接函数
int tcp_connect(const char *ip)
{
//向系统注册新的socket用于通信,sfd是该socket的fd
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd == -1)
{
perror("build a new socket failed!\n");
exit(-1);
}
//用于将新的socket的sfd,连接至一个网络地址,也就是服务器对应的网络地址,就是连接的过程
/*以下为几个结构体,定义的结构体,sockaddr_in最终会被强制转换为sockaddr
struct sockaddr
{
unsigned short int sa_family;
char sa_data[14];
};
struct socketaddr_in
{
unsigned short int sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr
{
uint32_t s_addr;
};
*/
//定义的serveraddr将用于连接网络
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(struct sockaddr));
//地址族是ipv4
serveraddr.sin_family = AF_INET;
//设置进程的端口号,用于和ip地址配合,进而唯一确定一个进程,进行通信
serveraddr.sin_port = htons(PORTNUMBER);
//将输入的ip地址转换为网络字节序列,例如192.168.1.1 转化为1100 0000.1010 1000.0000 0001.0000 0001
serveraddr.sin_addr.s_addr = inet_addr(ip);
//将sfd连接至指定的服务器网络地址serveraddr
if(connect(sfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr)) == -1)
{
perror("connect error\n");
close(sfd);
exit(-1);
}
return sfd;
}
int main(int argc,char *argv[])
{
//客户端开启需要三个参数:命令,ip
if(argc < 2)
{
printf("user:./clienttcp ip\n");
exit(-1);
}
//自定义函数 tcp_connect,将ip作为参数传入,函数返回新建的套接字的fd
int sfd = tcp_connect(argv[1]);
//尝试向服务器发送一些数据,检测是否连接成功
char buf[512] = {0};
//调用函数send,进行发送,参数为,本套接字fd,内容,内容长度,置为0的flag
send(sfd,"hello!SB",9,0);
//从服务器接收数据
recv(sfd,buf,sizeof(buf),0);
//显示接收到的数据
puts(buf);
//是不是该关闭,有待尝试
close(sfd);
}
服务器:
服务器负责的内容较客户端多,主要是,建立新的套接字,然后对套接字进行绑定,再监听客户端的连接请求,然后接受请求,算连接成功了
1.首先,执行可执行文件时,同样需要ip地址。
2.接着初始化,申请套接字,将这个套接字和该网络进行关联,也就是绑定。
3.接着对客户端的请求进行监听。
4.然后定义一个新的结构体,用于保存请求的客户端的信息,并申请新的socket,产生新的fd,即new_fd
5.连接成功的话,就可以正常的发送和接收信息了。
#include "5.tcp_net_socket.h"
#define PORTNUMBER 2333
void signalhandler(void);
int tcp_init(const char *ip,int port);
int tcp_accept(int sfd);
//用于信号处理,让服务器在按下ctrl + c 或者 ctrl + \时,不会退出
void signalhandler(void)
{
sigset_t sigSet;
sigemptyset(&sigSet);
sigaddset(&sigSet,SIGINT);
sigaddset(&sigSet,SIGQUIT);
sigprocmask(SIG_BLOCK,&sigSet,NULL);
}
//用于申请新的套接字,然后綁定相应网络,再对客户端的请求监听
int tcp_init(const char *ip,int port)
{
//服务端申请新的套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd == -1)
{
perror("build new socket failed!\n");
exit(-1);
}
//和客户端的connect类似,将套接字也与该网络地址联系起来,也就是綁定
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(struct sockaddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = inet_addr(ip);
if(bind(sfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr)) == -1)
{
perror("bind failed!\n");
close(sfd);
exit(-1);
}
//对客户端的连接进行监听,是否有申请连接?
if(listen(sfd,10) == -1)
{
perror("listen failed!\n");
close(sfd);
exit(-1);
}
return sfd;
}
int tcp_accept(int sfd)
{
//定义一个结构体,用于保存请求连接的客户端的ip 和 port
struct sockaddr_in clientaddr;
memset(&clientaddr,0,sizeof(struct sockaddr));
int addrlen = sizeof(struct sockaddr);
//服务器接受了客户端的请求,并且连接成功后,会创建新的套接字,fd为new_fd,
//这个new_fd将会被用来服务器向客户端的通信
int new_fd = accept(sfd,(struct sockaddr *)&clientaddr,&addrlen);
if(new_fd == -1)
{
perror("accept failed!\n");
close(sfd);
exit(-1);
}
printf("%s %d success connect....\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
return new_fd;
}
int main(int argc, char *argv[])
{
//服务器开启的时候也需要设置ip
if(argc < 2)
{
printf("usage:./servertop, ip\n");
exit(-1);
}
//用于信号处理,让服务器在按下ctrl + c 或者 ctrl + \时,不会退出
signalhandler();
//用于初始化套接字,返回该套接字的fd
int sfd = tcp_init(argv[1],PORTNUMBER);
//开始死循环,用于挂起服务器,实时接收客户端连接请求
while(1)
{
int cfd = tcp_accept(sfd);
char buf[512] = {0};
//接收客户端发送来的信息,在buf中保存
if(recv(cfd,buf,sizeof(buf),0) == -1)
{
perror("recv failed\n");
close(cfd);
close(sfd);
exit(-1);
}
puts(buf);
//从服务器向客户端发送数据,为客户端之间互发数据做准备
if(send(cfd,"hello, i'm server",17,0) == -1)
{
perror("send failed !\n");
close(cfd);
close(sfd);
exit(-1);
}
close(cfd);
}
close(sfd);
return 0;
}