您的位置:首页 > 理论基础 > 计算机网络

网络编程服务器与客户端实现代码详解

2017-08-11 20:25 423 查看
头文件定义

myhead.h

#ifndef _MYHEAD_H_
#define _MYHEAD_H_

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

#define MYPORT 6666 //1024以下是保留的端口号,用大于1024的
#define MYADDR "192.168.1.129"

#endif


服务器实现代码(带注释)

#include "myhead.h"

int main()
{
int socketfd = 0;       //文件描述符
int ret = 0;
int clientfd = 0;

//地址结构
struct sockaddr_in sock_server = {0};  //变量类型保存在netinet/in.h里面;
struct sockaddr_in sock_client = {0};

//typedef unsigned int socklen_t
socklen_t len = sizeof(struct sockaddr); //sockaddr的字节大小,结构体内部变量的排序顺序不同sizeof的结果也不同,换行不换行的问题

//第一步,创建套接字:
/*int socket(int domain, int type, int protocol);
domain:地址族;AF_INET / PF_INET表示的是ipv4,AF_INET6 / PF_INET6表示的是ipv6;
type:
指定通信的类型;
对于我们现阶段的常用的有SOCK_STREAM(顺序、可靠,双向,基于连接的===》用于TCP)
SOCK_DGRAM(不是基于连接的,不是可靠的===》UDP)
protocol:对于特定的套接字一般只提供一个协议,这个时候就可以把protocol赋值为0;
返回值:
成功返回文件描述符,失败返回-1;*/

socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == socketfd)
{
perror("socket");
return -1;
}
printf("socket success...\n");

//第二步,给套接字绑定必要信息:

/* struct sockaddr_in
{
short int sin_family;          Internet地址族 <===>u_short sa_family;
unsigned short int sin_port;   端口号
struct in_addr sin_addr;       IP地址
unsigned char sin_zero[8];     填0
};*/

/* struct in_addr
{
in_addr_t s_addr;       //保存IP地址,32位IPv4地址,网络字节序 unsigned long int <==> in_addr_t;
};*/

sock_server.sin_family = AF_INET;          //ipv4
sock_server.sin_port = htons(MYPORT);      //6666 1024以下是保留的端口号,用大于1024的
sock_server.sin_addr.s_addr = inet_addr(MYADDR); //inet_addr()用于将网络地址转化成二进制数字

/*int bind(int sockfd,const struct sockaddr *my_addr, socklen_t addrlen);
功能:为套接字绑定地址族、IP地址和端口号;
参数:
sockfd:socket函数返回的文件描述符;
my_addr:结构体指针,它指向的结构体保存地址族、IP地址和端口号;
addrlen:是sockaddr的字节大小= sizeof(struct sockaddr);  sockaddr <==> sockaddr_in;
返回值:
成功返回0,失败返回-1;

注意:(struct sockaddr *)只是防止编译警报;*/

ret = bind(socketfd, (struct sockaddr *)&sock_server, len/*sizeof(struct sockaddr_in)*/);
if (-1 == ret)
{
perror("bind");
return -1;
}
printf("bind success...\n");

//第三步,进行监听
/*int listen(int sockfd, int backlog);
功能:设置挂起队列的数量,进行监听 等待链接;
参数:
sockfd:socket函数返回的文件描述符;
backlog:挂起队列的最大长度;

返回值:
成功返回0,失败返回-1;
注意点:listen只能够用于SOCK_STREAM和 SOCK_SEQPACKET=====》不能够用于SOCK_DGRAM;
listen()并未开始接受连线,只是设置Socket为listen模式,真正接收client端连线的是accept()*/

ret = listen(socketfd, 10);
if (-1 == ret)
{
perror("listen");
return -1;
}
printf("listen success...\n");

//第四步,接受Socket连线
/*int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:接受请求,接受Socket连线;
参数:
sockfd:socket返回的文件描述符;
addr:用来保存客户端那边的地址族、IP地址和端口号;
addrlen:是addr的字节大小;
返回值:
成功返回一个新的文件描述符===》有客户端来连接服务器,accept会创建一个新的文件描述符来表示客户端;
例子:有A B C客户端来连接服务器,acceppt调用之后,会分别给A B C再用一个新的数字来表示,比如给A是4 ,B是5,C是6;
失败返回-1;
注意点:只能够用于基于连接的协议;===》SOCK_STREAM和 SOCK_SEQPACKET
如果没有客户端连接的时候,会阻塞在accept处!*/

clientfd = accept(socketfd, (struct sockaddr *)&sock_client, &len);
if (-1 == clientfd)
{
perror("accept");
return -1;
}
printf("accept success...clinet fd = %d\n", clientfd);

char recvbuff[20] = {0};
int recvcnt = 0;

while(1)
{
//      bzero(recvbuff, sizeof(recvbuff));     //bzero(void *s, int n)置字节字符串s的前n个字节为零且包括‘\0’。等价于memset(recvbuff, 0, sizeof(recvbuff))

memset(recvbuff, 0, sizeof(recvbuff));

/*接收数据:
ssize_t recv(int s, void *buf, size_t len, int flags);
功能:从套接字上接收数据;
参数:
s:就是accept返回的文件描述符,也就是客户端的文件描述符!
buf:用来保存数据的缓冲区;
len:保存数据的长度;
flags:参照send,一般都是设为0;
返回值:
成功返回接收到的字节大小,失败返回-1;*/

recvcnt = read(clientfd, recvbuff, sizeof(recvbuff));   //当recv中的flags为0的时候,recv = read;
if (-1 == recvcnt)
{
perror("recv");
return -1;
}
else
{
printf("Recv from Client %d bytes, data: %s\n", recvcnt, recvbuff);
}
if (0 == strcmp(recvbuff, "end"))
{
close(socketfd);
close(clientfd );
break;
}
}

return 0;
}


客户端实现代码(带注释)

#include "myhead.h"

int main()
{
int socketfd = 0;
int ret = 0;

struct sockaddr_in sock_server = {0};

//第一步,创建套接字:
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == socketfd)
{
perror("socket");
return -1;
}
printf("socket success...\n");

//用sock_server提醒你们这边链接的是服务器的IP地址和端口号;
sock_server.sin_family = AF_INET;
sock_server.sin_port = htons(MYPORT);
sock_server.sin_addr.s_addr = inet_addr(MYADDR);

//第二步,连接服务器
/*int connect(int  sockfd,const struct sockaddr *serv_addr, socklen_t addrlen);
功能:连接服务器;
参数:
sockfd:socket返回的文件描述符;
server_addr:保存的是要连接的服务器端的地址族、IP地址、端口号!
addrlen:就是server_addr的结构体大小;
返回值:
成功返回0,失败返回-1;*/
ret = connect(socketfd, (struct sockaddr *)&sock_server, sizeof(struct sockaddr));
if (-1 == ret)
{
perror("bind");
return -1;
}

/*比较bind和Connect的区别:
相同点:函数参数和返回值都是一样的;
不同点:
1、功能是不一样的;bind是给服务器程序绑定所运行电脑的IP地址,以及设定端口号;connect连接另外一台电脑上的服务器
2、bind绑定的是服务器所运行的电脑(A:192.168.1.102)的IP地址,设定端口号!
Connect在客户端调用,客户端运行在B(192.168.1.100)电脑上,因为connect是连接服务器的===》服务器由运行在
电脑A上===》所以Connect连接的是A电脑的IP地址、端口号;而不是B电脑的IP地址和端口号!*/

char sendbuff[20] = {0};
int sendcnt = 0;

while (1)
{
printf("Please input a string:\n");
scanf("%s", sendbuff);

/*ssize_t send(int s, const void *buf, size_t len, int flags);
功能:向另外一个套接字发送数据;
参数:
s:套接字;
buf:用来存储要发送的数据;
len:要发送数据的大小;
flags:参照man手册里面的内容,可以用 | 操作使用多个flags;===>一般都是使用0;
返回值:
成功返回发送的字节数;失败返回-1;*/

sendcnt = write(socketfd, sendbuff, /*strlen(sendbuff)*/ sizeof(sendbuff));
if (-1 == sendcnt)
{
perror("send");
return -1;
}
else
{
printf("Send to Server %d bytes, data: %s\n", sendcnt, sendbuff);
}

if (0 == strcmp(sendbuff, "end"))
{
close(socketfd);
break;
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络编程 服务器