网络编程服务器与客户端实现代码详解
2017-08-11 20:25
423 查看
头文件定义
myhead.h
服务器实现代码(带注释)
客户端实现代码(带注释)
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; }
相关文章推荐
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- C# 网络编程之Tcp实现客户端和服务器聊天
- Linux网络编程:客户端/服务器的简单实现
- Linux 网络编程基础---------------客户端/服务器的简单实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- 网络编程 实现服务器与客户端通讯
- 在java网络编程中实现服务器和客户端一对一的聊天
- Linux 网络编程实现TCP协议下的服务器客户端通信
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- linux网络编程--服务器客户端(TCP实现)
- 【python】网络编程-SocketServer 实现客户端与服务器间非阻塞通信
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- JavaSE的有关网络编程的服务器与客户端的通信代码
- Python 网络编程---简单的服务器与客户端实现---阻塞式编写
- Linux 网络编程基础 客户端/服务器的简单实现
- 网络编程:使用Socket实现简单的服务器和客户端的通信
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- 网络编程资料总结(二)----Tcp多线程服务器和客户端的实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现