经过几天高强度的学习,对套接字的编程有了初步的认识,今天对这几天所学的知识总结一下;首先简单阐述一下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. linux内核算法---hex_to_bin分享

    这是我从内核抠出来的一段代码,用处就是传入一个字符,即可以用printf语句%d以十进制数的格式输出,同时也可以以%p地址的形式输出. 代码如下: #include <stdio.h> # ...

  2. Markdown语法及编辑器

    宗旨 Markdown 的目标是实现「易读易写」. 可读性,无论如何,都是最重要的.一份使用 Markdown 格式撰写的文件应该可以直接以纯文本发布,并且看起来不会像是由许多标签或是格式指令所构成. ...

  3. 关于UIView中相关坐标及改变的相关方法

    // 将像素point由point所在视图转换到目标视图view中,返回在目标视图view中的像素值 - (CGPoint)convertPoint:(CGPoint)point toView:(UI ...

  4. LeetCode(52)-Remove Linked List Elements

    题目: Remove all elements from a linked list of integers that have value val. Example Given: 1 --> ...

  5. Understanding the Objective-C Runtime

    Wednesday, January 20, 2010 Understanding the Objective-C Runtime The Objective-C Runtime is one of ...

  6. golang 常见疑惑总结

    经常会有一些朋友问go语言的一些问题和疑惑,其实好多问题在官方文档和stackoverflow里都有详细的讲解,只要你肯花时间读一遍官方文档和Effective Go基本上都有找到答案.本文总结一下大 ...

  7. MySQL中的行级锁,表级锁,页级锁

    在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足. 在数据库的锁机制中介绍过,在DBMS中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引 ...

  8. 春天JDBC事务管理

    JDBC事务管理 春天提供编程式的事务管理(编程式事务管理)与声明式的事务管理(声明式事务management),为不同的事务实现提供了一致的编程模型,这节以JDBC事务为例,介绍Spring的事务管 ...

  9. sql语言不经常用,复习

    sql语言不经常用,每次再用都隔好久的时间,以致最基本的都想不起来了,只好转一篇记着= - 找的时候方便 SQL分类:  DDL-数据定义语言(CREATE,ALTER,DROP,DECLARE)  ...

  10. 初识Java——循环语句

    循环语句就是在一定条件下反复执行某一个操作.具体有三种方法实现: 1while循环语句 while语句也称作条件判断语句,它的循环方式为利用一个条件来控制是否要反复执行.语法如下: while(条件语 ...