经过几天高强度的学习,对套接字的编程有了初步的认识,今天对这几天所学的知识总结一下;首先简单阐述一下tcp通信;

  TCP提供的是可靠的,顺序的,以及不会重复的数据传输,处理流控制,由于TCP是可靠的,连接的,顺序的,所以TCP一般用于都应用于对传输的完整性,正确性要求严的场合;编写基于tcp的服务器-客户端模型的程序简单流程如下:

  服务器端:

  (1)调用socket()创建一个套接口

  (2)调用bind()函数是服务器进程与一个端口绑定

  (3)调用listen()设置客户接入队列的大小

  (4)调用accept()接受一个连接,如果介入的队列不为空,则返回一个已连接的套接口描述符,

  (5)调用sned()和recv()函数用来在已连接的套接口间进行发送和接收数据

  客户端:

  (1)调用socket()创建套接字

  (2)调用connect()函数向服务器发送连接请求;

  (3)调用send()函数和recv()函数

  下面是服务器端的代码;

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h> //server
int main()
{
int fd = ;
int nfd = ;
int ret = ;
unsigned char data[] = {}; fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd < ) {
perror("socket");
return ;
}
/*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = htonl(0xc0a8010a);//192.168.1.10
struct sockaddr_in clt;
int len = ; /*绑定*/
ret = bind(fd, (struct sockaddr *)&ser, );
if(ret < ) {
perror("bind");
return ;
} /*监听*/
ret = listen(fd, );
if(ret == -) {
perror("listen");
return ;
} /*接收连接,并且返回一个新的套接字描述符nfd*/
nfd = accept(fd, (struct sockaddr *)&clt, &len);
if(ret < ) {
perror("accept");
return ;
} /*接收*/
ret = recv(nfd, data, , );
if(ret < ) {
perror("recv");
return ;
}
printf("clt said: %s\n", data);
close(fd); return ;
}

服务器端

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> //client
int main()
{
int sock_fd = ;
int ret = ;
unsigned char *buf = "hello, hao ara you"; /*创建一个套接口*/
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < ) {
perror("socket");
return ;
} /*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = inet_addr("192.168.1.10"); //建立链接
ret = connect(sock_fd, (struct sockaddr *)&ser, );
if(ret < ) {
perror("connect");
return ;
} /*发送*/
ret = send(sock_fd, buf, strlen(buf), );
if(ret < ) {
perror("send");
return ;
} close(sock_fd);
return ;
}

客户端

上面程序是基于tcp的简单通信,下面我们利用tcp实现一个服务器多个客户机;要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过 accept函数接受并创建一个线程来处理。线程的创建函数为int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);

  下面是这个程序的源码

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h> #define PORT 9527 void *function(void *d);//线程要执行的函数 int main()
{
/*创建套接口*/
pthread_t pid= ;
int nfd = ;
int fd = ;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd < ) {
perror("sock");
return ;
}
/*服务器信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
ser.sin_addr.s_addr = inet_addr("192.168.1.10");
struct sockaddr_in clt;
int len = ; /*绑定*/
int ret = bind(fd, (struct sockaddr *)&ser, );
if(ret == -) {
perror("bind");
return ;
} /*监听*/
ret = listen(fd, );
if(ret < ) {
perror("listen");
return ;
} while() {
/*接受链接*/
nfd = accept(fd, (struct sockaddr *)&clt, &len);
if(nfd < ) {
perror("accept");
return ;
} /*创建一个线程*/
ret = pthread_create(&pid, NULL, function, (void *)nfd);
if(ret != ) {
perror("pthread_create");
return ;
} pthread_join(pid, NULL); close(nfd);
} close(fd);
return ;
} void *function(void *d)
{
unsigned char buf[] = {};
int nfd = (int )d;
int ret = ; memset(buf, , );
ret = recv(nfd, buf, , );
if(ret < ) {
perror("recv");
return NULL;
}
printf("client said: %s\n", buf); ret = send(nfd, "recv ok", , );
if(ret < ) {
perror("send");
return NULL;
} }

server

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h> #define PORT 9527 int main(int argc, char **argv)
{
if(argc != ) {
printf("using %s <ip address> <message>\n", argv[]);
return ;
}
/*创建套接口*/
int fd = ;
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd < ) {
perror("socket");
return ;
} /*服务器信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(PORT);
ser.sin_addr.s_addr = inet_addr(argv[]); /*创建链接*/
int ret = connect(fd, (struct sockaddr *)&ser, );
if(ret < ) {
perror("connect");
return ;
} /*访问*/
ret = send(fd, argv[], strlen(argv[]), );
if(ret < ) {
perror("send");
return ;
} char buf[] = {};
ret = recv(fd, buf, , );
if(ret < ) {
perror("recv");
return ;
}
printf("server: %s\n", buf);
close(fd); return ;
}

client

  上面代码需要注意的是,监听程序最大允许接受10个连接请求,如果这十个一直连接不断开的话,后续的连接请求就无法得到处理,所以我们需要在每次请求完毕之后就关闭nfd;下次请求再重新连接;

  第三个程序我们实现基于tcp的聊天程序:

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h> int main()
{
/*创建套接口*/
int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(fd < ){
perror("socket");
exit(EXIT_FAILURE);
} /*服务端信息*/
struct sockaddr_in srv;
srv.sin_family = AF_INET;
srv.sin_port=htons();
srv.sin_addr.s_addr = htonl(INADDR_ANY); /*绑定*/
int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
ret = listen(fd,);
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
struct sockaddr_in snd;
int snd_len = ; /*接受*/
int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
if(nfd < ){
perror("accpet");
exit(EXIT_FAILURE);
} char data[] = {};
char revdata[] = {};
/*聊天*/
while(){
memset(revdata, , );
memset(data, , );
ret = recv(nfd,revdata,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
} printf("client say: %s\n",revdata);
if(strcmp(revdata, "end") == ) {
break;
} ret = read(,data,);
if(ret < ){
perror("read");
exit(EXIT_FAILURE);
}
ret = send(nfd,data,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
} }
close(nfd);
close(fd);
return ;
}

server

 #include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h> int main()
{
/*创建套接口*/
int sock_fd = ;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < ) {
perror("socket");
return ;
}
/*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = inet_addr("192.168.1.10"); /*建立链接*/
int ret = connect(sock_fd, (struct sockaddr *)&ser, );
if(ret == -) {
perror("connect");
return ;
} /*聊天*/
unsigned char data[] = {};
unsigned char rec[] = {};
while() {
memset(data, , );
memset(rec, , );
int r_size = read(, data, );
if(r_size < ) {
perror("read");
return ;
} ret = send(sock_fd, data, strlen(data), );
if(ret < ) {
perror("send");
return ;
} ret = recv(sock_fd, rec, , );
if(ret < ) {
perror("recv");
return ;
}
printf("server said: %s\n", rec);
}
close(sock_fd);
return ;
}

client

上面这个代码存在的缺陷是,发送方跟接收只能发送一句接收一句,不能一次性发送多句,要解决这个问题就要需用到IO多路服用,可以通过这个函数来实现:
  int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

下面贴出代码:

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h> int main()
{
/*创建套接口*/
int fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(fd < ){
perror("socket");
exit(EXIT_FAILURE);
} /*服务端信息*/
struct sockaddr_in srv;
srv.sin_family = AF_INET;
srv.sin_port=htons();
srv.sin_addr.s_addr = htonl(INADDR_ANY); /*绑定*/
int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
ret = listen(fd,);
if(ret < ){
perror("bind");
exit(EXIT_FAILURE);
}
struct sockaddr_in snd;
int snd_len = ; /*接受*/
int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
if(nfd < ){
perror("accpet");
exit(EXIT_FAILURE);
} fd_set rfds;
char data[] = {};
char revdata[] = {};
/*聊天*/
while(){ FD_ZERO(&rfds);
FD_SET(,&rfds);
FD_SET(nfd,&rfds);
ret = select(nfd+,&rfds,NULL,NULL,NULL);
if(FD_ISSET(nfd, &rfds)){
ret = recv(nfd,revdata,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
} printf("client say: %s\n",revdata);
if(strcmp(revdata, "end") == ) {
break;
}
}
if(FD_ISSET(, &rfds)) {
ret = read(,data,);
if(ret < ){
perror("read");
exit(EXIT_FAILURE);
}
ret = send(nfd,data,,);
if(ret < ){
perror("recv");
exit(EXIT_FAILURE);
}
}
memset(revdata, , );
memset(data, , );
}
close(nfd);
close(fd);
return ;
}

server

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> int main()
{
/*创建套接口*/
int sock_fd = ;
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < ) {
perror("socket");
return ;
}
/*服务端信息*/
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons();
ser.sin_addr.s_addr = inet_addr("192.168.1.10"); /*建立链接*/
int ret = connect(sock_fd, (struct sockaddr *)&ser, );
if(ret == -) {
perror("connect");
return ;
} /*聊天*/
fd_set rfds;
unsigned char data[] = {};
unsigned char rec[] = {};
while() {
memset(data, , );
memset(rec, , );
FD_ZERO(&rfds); //清空
FD_SET(,&rfds);//(标准输入)
FD_SET(sock_fd,&rfds);//添加监听描述符(套接字)
/*多路复用IO*/
ret = select(sock_fd+,&rfds,NULL,NULL,NULL); if(FD_ISSET(, &rfds)){//监听键盘是否有输入,执行接收
int r_size = read(, data, );
if(r_size < ) {
perror("read");
return ;
}
ret = send(sock_fd, data, strlen(data), );
if(ret < ) {
perror("send");
return ;
}
} if(FD_ISSET(sock_fd, &rfds)) {//监听sock_fd是否有输入,执行接收
ret = recv(sock_fd, rec, , );
if(ret < ) {
perror("recv");
return ;
}
printf("server said: %s\n", rec);
}
}
close(sock_fd);
return ;
}

client

    

  

网络编程之套接字(tcp)的更多相关文章

  1. TCP/IP网络编程之套接字类型与协议设置

    套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...

  2. 【TCP/IP网络编程】:01理解网络编程和套接字

    1.网络编程和套接字 网络编程与C语言中的printf函数和scanf函数以及文件的输入输出类似,本质上也是一种基于I/O的编程方法.之所以这么说,是因为网络编程大多是基于套接字(socket,网络数 ...

  3. TCP/IP网络编程之网络编程和套接字

    网络编程和套接字 网络编程又称为套接字编程,就是编写一段程序,使得两台连网的计算机彼此之间可以交换数据.那么,这两台计算机用什么传输数据呢?首先,需要物理连接,将一台台独立的计算机通过物理线路连接在一 ...

  4. UNIX网络编程——原始套接字(dos攻击)

    原始套接字(SOCK_RAW).应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能. 注意原始套接字只能够由有 root权限的人创建. 可以参考前面的博客<<UNIX网络 ...

  5. UNIX网络编程——原始套接字的魔力【续】

    如何从链路层直接发送数据帧 上一篇里面提到的是从链路层"收发"数据,该篇是从链路层发送数据帧. 上一节我们主要研究了如何从链路层直接接收数据帧,可以通过bind函数来将原始套接字绑 ...

  6. Linux网络编程——原始套接字实例:MAC 头部报文分析

    通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...

  7. 19 网络编程--Socket 套接字方法

    1.Socket(也称套接字)介绍 socket这个东东干的事情,就是帮你把tcp/ip协议层的各种数据封装啦.数据发送.接收等通过代码已经给你封装好了 ,你只需要调用几行代码,就可以给别的机器发消息 ...

  8. 网络编程--Socket(套接字)

    网络编程 网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯.网络编程中 有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后 如何可靠高效的进行数据传输.在 ...

  9. 网络编程之套接字socket

    目录 socket套接字 引子 为何学习socket一定要先学习互联网协议 socket是什么 套接字类型 基于文件类型的套接字家族 基于网络类型的套接字家族 套接字工作流程 基于TCP的套接字 简单 ...

  10. 8.7 day28 网络编程 socket套接字 半连接池 通信循环 粘包问题 struct模块

    前置知识:不同计算机程序之间的数据传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...

随机推荐

  1. 【Android 应用开发】Android中的回调Callback

    回调就是外部设置一个方法给一个对象, 这个对象可以执行外部设置的方法, 通常这个方法是定义在接口中的抽象方法, 外部设置的时候直接设置这个接口对象即可. 例如给安卓添加按钮点击事件, 我们创建了OnC ...

  2. linux下安装ruby版本管理器RVM

    这里以ubuntu为例. 直接以如下命令行安装可能会不成功,因为rvm.io站点有时会被墙: curl -sSL https://get.rvm.io | bash -s stable #或者 cur ...

  3. WPF如何得到一个在用户控件内部的元素的坐标位置

    例如有这样一个用户控件: <UserControl d:DesignHeight="100" d:DesignWidth="200" ...> &l ...

  4. merge intervals(合并间隔)

    Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[2,6],[8,1 ...

  5. Java Selenium 定位元素 实现的一个注册功能

    import java.util.List; import java.util.concurrent.TimeUnit; import org.openqa.selenium.Alert; impor ...

  6. MySQL基本sql语句

    MySQL基本操作语句 操作文件夹(库) 增加create database 库名 charset utf8;charset utf8是指定库的字符编码删除drop database 库名删除某个数据 ...

  7. Day7 面向对象和类的介绍

    面向对象讲解: ''' 面向过程: 核心是过程二字,过程指的是问题的解决步骤,基于过程去设计程序,就好比在设计一条流水线,是一种机械式的思维方式. 优点:复杂的问题流程化,进而简单化 缺点:可扩展性差 ...

  8. svn path already exists的解决办法

    这种问题的一般原因是这个path所指的目录在服务器端是一个空目录,对客户端不可见,客户端如果新建了这个目录,而且向服务器端commit的时候就会报错,服务器端此目录已存在,这个时候就会存在一个问题:就 ...

  9. 拖拽模块move1

    刚开的博客,想着写点什么,以前写过拖拽函数,后来又学习了模块化,于是一直想把之前写的拖拽函数封成一个独立的模块,方便以后调用,说干就干,下面码代码... <script> var move ...

  10. 印钞机 V1.0(量化选基总结)

    今年的元旦,在家把之前手工的选基方法完全程序化了.这是我的"印钞机" V1.0. 为什么叫印钞机,详细情况可见下文及最后的总结. 量化选基成果 我的主要基金投资方法其实就是量化选基 ...