实验平台

linux

实验内容

编写TCP服务器和客户端程序,程序运行时服务器等待客户端连接。一旦连接成功,服务器显示客户端的IP地址和端口号,并向客户端发送字符串

实验原理

TCP是面向连接的通信,其主要实现过程如下:

我们将服务器代码分为两部分。

1. init_tcp_server() tcp服务器的初始化

2. main() 实现读写数据

这样做的好处是main函数不必写的特别冗长,利于维护。从框架上来说,服务器的初始化也与读、写无关。

tcp服务器的初始化----init_tcp_server()

1. 创建socket

sockfd = socket(AF_INET, SOCK_STREAM, ); //AF_INT:ipv4, SOCK_STREAM:tcp协议

2. 设置socket  当然这一步可以省略

int32_t opt = ;
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

详细说明:

3. 绑定(bind函数)

将socket和地址(包括ip,port)绑定。需要定义一个结构体地址,以便于将port的主机字节序转化成网络字节序

struct sockaddr_in serveraddr;    //地址结构体

bind函数

bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))

4. listen监听,将接收到的客户端放入队列

 listen(sockfd,)  //第二个参数是队列长度

5. 调用accept函数,从队列获取请求,返回socket描述符,如果没有请求(没有客户端连接),将会阻塞,直到获取请求

int fd=accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);

至此服务器初始化完成,返回成功连接的套接字fd。

服务器端代码如下:tcpserver.c

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h> #define PORT 1234
#define BACKLOG 10
#define BUFFER_SIZE 100 /**
* @brief 初始化tcp服务器
* @param[in] listenfd 监听套接字
* @return -1 - 失败, socket 文件句柄 - 成功
*/
int32_t init_tcp_server(int32_t listenfd)
{
struct sockaddr_in server;
struct sockaddr_in client; int32_t connectfd = ;
int32_t addrlen;
int32_t ret = ;
addrlen = sizeof(client); /**< 创建一个tcp套接字 */
listenfd = socket(AF_INET, SOCK_STREAM, );
if (listenfd == -)
{
perror("create socket failed!\n");
exit();
} /**< 设置一个tcp套接字 */
int32_t opt = ;
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (ret < )
{
perror("set socket failed!\n");
exit();
} /**< 设置服务器监听所有的IP地址 */
bzero(&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(PORT); /**< 主机字节序转化成网络字节序 */
server.sin_addr.s_addr = htonl(INADDR_ANY); /**< 与服务器进行绑定 */
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -)
{
perror("bind error");
exit();
} /**< 监听 */
if (listen(listenfd, BACKLOG) == -)
{
perror("listen error");
exit();
} /**< 等待客户端连接,如果没有,一直阻塞 */
if ((connectfd = accept(listenfd, (struct sockaddr *)&client, &addrlen)) == -)
{
perror("accept error");
close(listenfd);
close(connectfd);
exit();
}
printf("You got a connection from client's ip is %s, port is %d\n", inet_ntoa(client.sin_addr), htons(client.sin_port)); return connectfd;
} int main()
{
int32_t listenfd = ;
int32_t connectfd = ;
char buf[BUFFER_SIZE] = "Welcome to my server"; connectfd = init_tcp_server(listenfd); send(connectfd, buf, BUFFER_SIZE, ); /**< 发送信息到客户端 */
close(connectfd);
close(listenfd);
}

客户端

同样,将客户端代码分成两部分:

1. init_tcp_client() tcp客户端的初始化

2. main() 实现读写数据

客户端的初始化较为简单,如上图,只要实现socket和connect函数即可。但是我们希望可以手动输入客户端连接的IP地址,便于以后扩展,因此需要给客户端初始化传入一个参数。例如,输入:

./tcpclient 127.0.0.1

客户端代码如下:tcpclient.c

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h> #define PORT 1234
#define BUFFER_SIZE 100 int32_t init_tcp_client(char *ipaddr)
{
int sockfd = ;
struct sockaddr_in server; if ((sockfd = socket(AF_INET, SOCK_STREAM, )) == -)
{
perror("create socket failed!\n");
exit();
} bzero(&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
inet_pton(AF_INET, ipaddr, &server.sin_addr.s_addr); /**< 点分十进制转换成二进制的网络字节序 */ if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -)
{
perror("connect error");
exit();
} return sockfd;
} int32_t main(int argc, char*argv[])
{
int32_t sockfd, num;
char buf[BUFFER_SIZE];
if (argc != )
{
printf("Usage:%s <IP Address>\n",argv[]);
exit();
}
sockfd = init_tcp_client(argv[]); if ((num = recv(sockfd, buf, BUFFER_SIZE, )) == -)
{
perror("recv error");
exit();
} buf[num - ] = '\0';
printf("Server Message: %s\n", buf);
close(sockfd); return ;
}

Makefile文件如下:

all:server client

server:tcpserver.c
gcc tcpserver.c -o server client:tcpclient.c
gcc tcpclient.c -o client clean:
rm -rf server client

实验结果如下:

基于TCP的客户端和服务器端的代码设计的更多相关文章

  1. 基于UDP的客户端和服务器端的代码设计

    实验平台 linux 实验内容 编写UDP服务器和客户端程序,客户端发送消息,服务器接收消息,并打印客户端的IP地址和端口号. 实验原理 UDP是无需连接的通信,其主要实现过程如下: 同样,我们可以按 ...

  2. 基于TCP的客户端、服务器端socket编程

    一.实验目的 理解tcp传输客户端服务器端通信流程 二.实验平台 MAC OS 三.实验内容 编写TCP服务器套接字程序,程序运行时服务器等待客户的连接,一旦连接成功,则显示客户的IP地址.端口号,并 ...

  3. UDP广播的客户端和服务器端的代码设计

    实验环境 linux 注意: 使用UDP广播,是客户端发送广播消息,服务器端接收消息.实际上是客户端探测局域网中可用服务器的一种手段.客户端发送,服务器端接收,千万不能弄混淆!!! 为了避免混淆,本文 ...

  4. 使用CXF开发WebService程序的总结(四):基于bean的客户端和服务端代码的编写

    1. 在原服务端项目 ws_server中添加两个bean 1.1 添加两个类  User 和 Clazz   package com.lonely.pojo; public class User { ...

  5. 实验09——java基于TCP实现客户端与服务端通信

    TCP通信         需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手        底层通过 流 发送数据 数据没有大小限制        可靠的传输机制 - 丢包重发 包的顺序的 ...

  6. C/S模型:TCP,UDP构建客户端和服务器端(BIO实现

    Java中提供了socket编程来构建客户端和服务器端 TCP构建服务器端的步骤:(1)bind:绑定端口号(2)listen:监听客户端的连接请求(3)accept:返回和客户端连接的实例(4)re ...

  7. 【TCP/IP网络编程】:04基于TCP的服务器端/客户端

    摘要:结合前面所讲述的知识,本篇文章主要介绍了简单服务器端和客户端实现的框架流程及相关函数接口. 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字(本 ...

  8. C/C++网络编程5——实现基于TCP的服务器端/客户端2

    三次握手过程详解: 1:客户端的协议栈向服务器端发送SYN包,并告诉服务器端当前放送序号为j,客户端进入SYNC_SEND状态. 2:服务器端的协议栈收到这个包以后,和客户端进行ACK应答,应答值为j ...

  9. 基于TCP的安卓客户端开发

    一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...

随机推荐

  1. Java第三十三天,IO操作(续集),字符转换流

    计算机都是以二进制码格式存储文件的,但是在读写文件的过程中,每个应用程序都有自己的编码格式.FileWrite和FileRead类是通过查询系统默认码表进行读写的,因此在自己的系统上能够实现编码的智能 ...

  2. Java第十五天,泛型

    一.定义 泛型是一种未知的数据类型,即当我们不知道该使用哪种数据类型的时候,可以使用泛型. 泛型的本质是为了  参数化 类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型) ...

  3. MySQL学习之路1-Mac下启动连接MySQL服务

    MySQL简介 (MySQL是目前最流行的关系型数据库管理系统,现属于Oracle公司.) MySQL主要特点: 支持大型数据库,支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64 ...

  4. Python Requests-学习笔记(10)-会话对象

    会话对象让你能够跨请求保持某些参数.它也会在同一个Session实例发出的所有请求之间保持cookies. 会话对象具有主要的Requests API的所有方法. 我们来跨请求保持一些cookies: ...

  5. 判断一组checkbox/redio是否被选中,为其添加样式

    业务场景:当一行中有一个CheckBox被选中,则为此行添加class. <script type="text/javascript"> $(function(){ $ ...

  6. Nexus3 集成 crowd 插件

    公司使用的软件开发和协作工具为 Atlassian 系列软件,所以统一使用 crowd 来实现统一登录(SSO). crowd 配置 具体操作细节见我之前写的 Atlassian 系列软件安装(Cro ...

  7. 如何使用Three.js加载obj和mtl文件

    OBJ和MTL是3D模型的几何模型文件和材料文件. 在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃. 现在要加载OBJ和MTL文件,需要结合OBJLoader和MTL ...

  8. HashMap之KeySet分析

    本篇涵盖 1.HashMap并不是用keySet来存储key的原因及证明 2.keySet方法返回后的remove.add操作原理 一.方法作用 概括一下 1.keySet方法返回map中包含的键的集 ...

  9. K - Two Contests

    题目连接:https://atcoder.jp/contests/agc040/tasks/agc040_b 大佬题解:https://blog.csdn.net/duanghaha/article/ ...

  10. 用functools.lru_cache实现Python的Memoization

    现在你已经看到了如何自己实现一个memoization函数,我会告诉你,你可以使用Python的functools.lru_cache装饰器来获得相同的结果,以增加方便性. 我最喜欢Python的原因 ...